<template>
  <div class="home-container">
    <!-- 侧边栏 -->
    <div class="sidebar">
      <div class="user-profile" @click="showProfile = true">
        <div class="avatar">
          <el-avatar :size="50" :src="userInfo.avatar"/>
        </div>
        <div class="user-info">
          <div class="nickname">{{ userInfo.nickname }}</div>
          <div class="tf-id">TF ID: {{ userInfo.tfId }}</div>
        </div>
      </div>

      <el-menu
          :default-active="activeMenu"
          class="menu"
          @select="handleMenuSelect"
      >
        <el-menu-item index="friends">
          <el-icon>
            <ChatDotRound/>
          </el-icon>
          <span>我的好友</span>
        </el-menu-item>
        <el-menu-item index="contacts">
          <el-icon>
            <User/>
          </el-icon>
          <span>通讯录</span>
        </el-menu-item>
        <el-menu-item index="aiChat">
          <el-icon>
            <Service/>
          </el-icon>
          <span>AI助手</span>
        </el-menu-item>
      </el-menu>

      <div class="logout" @click="handleLogout">
        <el-icon>
          <SwitchButton/>
        </el-icon>
        <span>退出登录</span>
      </div>
    </div>

    <!-- 主内容区 -->
    <div class="main-content">
      <!-- 好友列表 -->
      <div v-if="activeMenu === 'friends'" class="friend-list">
        <div class="friend-list-header">
          <div class="friend-actions-header">
            <el-button
                type="primary"
                size="small"
                @click="showSearchUserDialog = true"
            >
              <el-icon>
                <Plus/>
              </el-icon>
              添加好友
            </el-button>
            <el-button
                type="info"
                size="small"
                @click="viewFriendRequests"
                class="friend-request-btn"
            >
              好友请求
              <template v-if="friendRequestCount > 0">
                <el-badge :value="friendRequestCount" class="request-badge" type="danger"/>
              </template>
            </el-button>
          </div>
          <div class="friend-search">
            <el-input
                v-model="friendSearchQuery"
                placeholder="搜索好友"
                clearable
                size="small"
            >
              <template #prefix>
                <el-icon>
                  <Search/>
                </el-icon>
              </template>
            </el-input>
          </div>
        </div>

        <div
            v-loading="loading"
            element-loading-text="加载中..."
            class="friend-list-content"
        >
          <div
              v-for="friend in filteredFriends"
              :key="friend.id"
              :class="['friend-item', selectedFriend && selectedFriend.userId === friend.userId ? 'active' : '']"
              @click="selectFriend(friend)"
          >
            <div class="avatar-container">
              <el-avatar :size="50" :src="friend.avatar"/>
              <div v-if="unreadMessages[friend.userId] && unreadMessages[friend.userId] > 0" class="unread-badge">
                <el-badge :value="unreadMessages[friend.userId]" type="danger"/>
              </div>
            </div>
            <div class="friend-info">
              <div class="friend-name">{{ friend.remark || friend.nickname }}</div>
              <div class="friend-status">{{ friend.signature || '暂无消息' }}</div>
            </div>
            <div class="friend-actions">
              <el-dropdown trigger="click" @command="handleFriendCommand($event, friend)">
                <div class="friend-more" @click.stop>
                  <el-icon>
                    <MoreFilled/>
                  </el-icon>
                </div>
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item command="detail">查看资料</el-dropdown-item>
                    <el-dropdown-item command="remark">修改备注</el-dropdown-item>
                    <el-dropdown-item command="delete" divided>删除好友</el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </div>
          </div>

          <!-- 加载更多 -->
          <div v-if="pagination.total > friendList.length" class="load-more">
            <el-button @click="loadMoreFriends" :loading="loading" text>加载更多</el-button>
          </div>

          <!-- 无好友时显示 -->
          <el-empty v-if="friendList.length === 0 && !loading" description="暂无好友">
            <el-button type="primary" size="small" @click="showSearchUserDialog = true">添加好友</el-button>
          </el-empty>
        </div>
      </div>

      <!-- 聊天内容区 -->
      <div v-if="selectedFriend && activeMenu === 'friends'" class="chat-area">
        <div class="chat-header">
          <div class="chat-title">{{ selectedFriend.remark || selectedFriend.nickname }}</div>
          <div class="chat-toolbar">
            <el-dropdown trigger="click" @command="handleChatCommand">
              <el-icon class="toolbar-icon">
                <MoreFilled/>
              </el-icon>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item command="clearChat">清空聊天记录</el-dropdown-item>
                  <el-dropdown-item command="viewProfile">查看资料</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </div>
        </div>
        <div class="chat-messages" v-loading="messageLoading" @scroll="handleChatScroll">
          <div v-if="messageList.length === 0" class="chat-placeholder">
            <el-empty description="暂无聊天记录"/>
          </div>
          <div v-else class="message-list">
            <!-- 加载更多提示 -->
            <div v-if="messagePagination.hasMore" class="load-more-messages">
              <div v-if="messageLoading" class="loading-indicator">
                <el-icon class="loading-icon">
                  <Loading/>
                </el-icon>
                加载更多消息...
              </div>
              <div v-else @click="loadMoreMessages" class="load-more-btn">
                点击加载更多
              </div>
            </div>

            <div
                v-for="message in messageList"
                :key="message.id"
                :class="['message-item', message.senderUserId == userInfo.id ? 'self' : 'other']"
            >
              <el-avatar
                  :size="40"
                  :src="message.senderUserId == userInfo.id ? userInfo.avatar : selectedFriend.avatar"
              />
              <div class="message-content">
                <div class="message-text">
                  <!-- 根据消息类型显示不同内容 -->
                  <template v-if="message.msgType === MessageType.TEXT">
                    {{ message.content }}
                  </template>

                  <template v-else-if="message.msgType === MessageType.IMAGE">
                    <!-- 如果是临时预览，直接使用内容中的数据URL -->
                    <img
                        v-if="message.isUploading"
                        class="message-image loading"
                        :src="message.content"
                        alt="正在上传图片..."
                    />
                    <!-- 文件已过期显示占位图 -->
                    <div v-else-if="isFileExpired(message.expireTime)" class="expired-image-container">
                      <el-icon class="expired-icon">
                        <Picture/>
                      </el-icon>
                      <p class="expired-text">图片已过期</p>
                    </div>
                    <!-- 直接显示图片 -->
                    <div v-else class="image-container">
                      <img
                          class="message-image"
                          :src="getImageUrl(message)"
                          :key="getImageKey(message)"
                          @click="previewImage(getImageUrl(message))"
                          alt="图片消息"
                          @error="handleImageError($event, message)"
                          @load="handleImageLoad($event, message)"
                      />
                      <!-- 显示文件信息 -->
                      <div v-if="message.fileName" class="file-info-overlay">
                        <div class="file-name-text">{{ message.fileName }}</div>
                        <div v-if="message.fileSize" class="file-size-text">{{ formatFileSize(message.fileSize) }}</div>
                      </div>
                      <el-button
                          class="download-button"
                          type="primary"
                          size="small"
                          @click.stop="(event) => downloadFileByUrl(message.filePath, event, message.expireTime)"
                      >
                        <el-icon>
                          <Download/>
                        </el-icon>
                        下载
                      </el-button>
                    </div>
                  </template>

                  <!-- 视频类型消息 -->
                  <template v-else-if="message.msgType === MessageType.VIDEO">
                    <!-- 如果是正在上传的临时视频 -->
                    <div v-if="message.isUploading" class="file-message uploading">
                      <el-icon class="file-icon">
                        <VideoCameraFilled/>
                      </el-icon>
                      <div class="file-info">
                        <div class="file-name">{{ message.fileName }}</div>
                        <div class="file-meta">
                          <span class="file-size">{{ formatFileSize(message.fileSize) }}</span>
                          <div class="upload-progress-container">
                            <el-progress
                                :percentage="message.uploadProgress"
                                :stroke-width="5"
                                :show-text="false"
                            />
                            <span class="progress-text">{{ message.uploadProgress }}%</span>
                          </div>
                        </div>
                      </div>
                    </div>
                    <!-- 视频已过期显示占位信息 -->
                    <div v-else-if="isFileExpired(message.expireTime)" class="expired-file-message">
                      <el-icon class="expired-icon">
                        <VideoCameraFilled/>
                      </el-icon>
                      <div class="file-info">
                        <div class="file-name">{{ message.fileName || getFileName(message.filePath) }}</div>
                        <div class="file-meta">
                          <span class="file-expire expired">视频已过期</span>
                        </div>
                      </div>
                    </div>
                    <!-- 视频文件显示 -->
                    <div v-else class="video-container">
                      <video
                          class="message-video"
                          :src="message.filePath"
                          controls
                          preload="metadata"
                          @click="$event => previewVideo(message.filePath, $event.target)"
                          style="transform: none !important; object-fit: contain;"
                          playsinline
                          webkit-playsinline
                      >
                        您的浏览器不支持视频播放
                      </video>
                      <!-- 显示文件信息 -->
                      <div class="file-info-overlay">
                        <div class="file-name-text">{{ message.fileName }}</div>
                        <div v-if="message.fileSize" class="file-size-text">{{ formatFileSize(message.fileSize) }}</div>
                      </div>
                      <el-button
                          class="download-button"
                          type="primary"
                          size="small"
                          @click.stop="(event) => downloadFileByUrl(message.filePath, event, message.expireTime)"
                      >
                        <el-icon>
                          <Download/>
                        </el-icon>
                        下载
                      </el-button>
                    </div>
                  </template>

                  <template v-else-if="message.msgType === MessageType.FILE">
                    <!-- 视频文件特殊处理 -->
                    <div v-if="isVideoFile(message.fileName)" class="video-container">
                      <video
                          class="message-video"
                          :src="message.filePath"
                          controls
                          preload="metadata"
                          @click="$event => previewVideo(message.filePath, $event.target)"
                          style="transform: none !important; object-fit: contain;"
                          playsinline
                          webkit-playsinline
                      >
                        您的浏览器不支持视频播放
                      </video>
                      <!-- 显示文件信息 -->
                      <div class="file-info-overlay">
                        <div class="file-name-text">{{ message.fileName }}</div>
                        <div v-if="message.fileSize" class="file-size-text">{{ formatFileSize(message.fileSize) }}</div>
                      </div>
                      <el-button
                          class="download-button"
                          type="primary"
                          size="small"
                          @click.stop="(event) => downloadFileByUrl(message.filePath, event, message.expireTime)"
                      >
                        <el-icon>
                          <Download/>
                        </el-icon>
                        下载
                      </el-button>
                    </div>
                    <!-- 如果是正在上传的临时文件 -->
                    <div v-else-if="message.isUploading" class="temp-file-container">
                      <div class="temp-file-info">
                        <el-icon class="file-icon">
                          <Document/>
                        </el-icon>
                        <span class="file-name">{{ message.fileName }}</span>
                        <span class="file-size">({{ formatFileSize(message.fileSize) }})</span>
                      </div>
                      <el-progress :percentage="message.uploadProgress"/>
                    </div>
                    <!-- 默认文件样式 -->
                    <div v-else class="file-message">
                      <el-icon class="file-icon">
                        <Document/>
                      </el-icon>
                      <div class="file-info">
                        <div class="file-name">{{ message.fileName || '未知文件' }}</div>
                        <div class="file-meta">
                          <span v-if="message.fileSize" class="file-size">{{ formatFileSize(message.fileSize) }}</span>
                          <span v-if="message.expireTime" class="file-expire">
                            {{
                              isFileExpired(message.expireTime) ? '已过期' : '有效期至 ' + formatDate(message.expireTime)
                            }}
                          </span>
                        </div>
                      </div>
                      <el-button
                          class="file-download-button"
                          type="primary"
                          size="small"
                          :disabled="isFileExpired(message.expireTime)"
                          @click="(event) => downloadFileByUrl(message.filePath, event, message.expireTime)"
                      >
                        <el-icon>
                          <Download/>
                        </el-icon>
                        下载
                      </el-button>
                    </div>
                  </template>

                  <!-- 语音消息类型 -->
                  <template v-else-if="message.msgType === MessageType.AUDIO">
                    <div class="audio-message">
                      <div class="audio-player">
                        <el-icon @click="playAudio(message)" class="play-icon">
                          <el-icon v-if="message.isPlaying">
                            <Loading/>
                          </el-icon>
                          <el-icon v-else>
                            <Microphone/>
                          </el-icon>
                        </el-icon>
                        <div class="audio-info">
                          <div class="audio-wave" :class="{ 'active': message.isPlaying }">
                            <span v-for="n in 5" :key="n" class="wave-bar"></span>
                          </div>
                          <div class="audio-duration">{{
                              message.duration ? formatDuration(message.duration) : '00:00'
                            }}
                          </div>
                        </div>
                        <el-button
                            class="download-button"
                            type="primary"
                            size="small"
                            @click.stop="(event) => downloadFileByUrl(message.filePath, event, message.expireTime)"
                        >
                          <el-icon>
                            <Download/>
                          </el-icon>
                          下载
                        </el-button>
                      </div>
                    </div>
                  </template>
                </div>
                <div class="message-time">
                  {{ formatDate(message.createTime) }}
                  <span
                      v-if="message.senderUserId == userInfo.id"
                      class="read-status"
                      :data-read="message.status === 1"
                  >
                    {{ message.status === 1 ? '已读' : '未读' }}
                  </span>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="chat-input-area">
          <div class="toolbar">
            <el-tooltip content="发送图片" placement="top">
              <el-icon @click="openImageSelector" class="toolbar-action">
                <PictureFilled/>
              </el-icon>
            </el-tooltip>
            <el-tooltip content="发送文件" placement="top">
              <el-icon @click="openFileSelector" class="toolbar-action">
                <Paperclip/>
              </el-icon>
            </el-tooltip>
            <el-tooltip content="录制语音" placement="top">
              <el-icon @click="startVoiceRecording" class="toolbar-action">
                <Microphone/>
              </el-icon>
            </el-tooltip>
            <input
                ref="imageInputRef"
                type="file"
                accept="image/*"
                style="display: none;"
                @change="handleImageSelected"
            />
            <input
                ref="fileInputRef"
                type="file"
                style="display: none;"
                @change="handleFileSelected"
            />
          </div>

          <!-- 临时图片预览 -->
          <div v-if="tempSelectedImage" class="temp-image-preview">
            <img :src="tempSelectedImage.previewUrl" class="temp-preview-img"/>
            <div class="temp-image-actions">
              <el-icon @click="tempSelectedImage = null" class="temp-image-delete">
                <Delete/>
              </el-icon>
            </div>
          </div>

          <!-- 临时文件预览 -->
          <div v-if="tempSelectedFile" class="temp-file-preview">
            <div class="temp-file-info">
              <el-icon class="file-icon">
                <Document/>
              </el-icon>
              <span class="temp-file-name">{{ tempSelectedFile.file.name }}</span>
              <span class="temp-file-size">({{ formatFileSize(tempSelectedFile.file.size) }})</span>
            </div>
            <div class="temp-file-actions">
              <el-icon @click="tempSelectedFile = null" class="temp-file-delete">
                <Delete/>
              </el-icon>
            </div>
          </div>

          <!-- 录音中界面 -->
          <div v-if="isRecording" class="recording-controls">
            <div class="recording-time">{{ formatDuration(recordingDuration) }}</div>
            <div class="recording-wave">
              <div v-for="n in 5" :key="n" class="recording-wave-bar"></div>
            </div>
            <el-button type="danger" size="small" @click="cancelVoiceRecording">取消</el-button>
            <el-button type="primary" size="small" @click="stopVoiceRecording">完成</el-button>
          </div>

          <!-- 文本输入框仅在没有选择图片或文件时显示 -->
          <el-input
              v-if="!tempSelectedImage && !tempSelectedFile && !isRecording"
              v-model="messageText"
              type="textarea"
              :rows="3"
              placeholder="输入消息..."
              resize="none"
              @keydown.enter.prevent="sendMessage"
          />
          <el-button
              type="primary"
              :disabled="(!messageText || !messageText.trim()) && !tempSelectedImage && !tempSelectedFile && !isRecording"
              @click="sendMessage"
          >
            发送
          </el-button>
        </div>
      </div>

      <!-- 搜索结果区域 -->
      <div v-if="searchResult && activeMenu === 'friends'" class="search-result-area">
        <div class="search-result-content">
          <div class="search-result-header">
            <h3>搜索结果</h3>
            <el-button type="primary" size="small" @click="searchResult = null">返回</el-button>
          </div>
          <div class="search-result-item">
            <el-avatar :size="60" :src="searchResult.avatar"/>
            <div class="search-result-info">
              <div class="search-result-name">
                {{ searchResult.nickname }}
                <span class="search-result-tfid">TF ID: {{ searchResult.tfId }}</span>
              </div>
              <div class="search-result-email">{{ searchResult.email }}</div>
              <div class="search-result-signature">{{ searchResult.signature || '这个人很懒，什么都没留下' }}</div>
              <div class="search-result-actions">
                <el-button
                    v-if="(!searchResult.isFriend || searchResult.isFriend === 2) && searchResult.id !== userInfo.id"
                    type="primary"
                    size="small"
                    @click="handleAddFriend(searchResult)"
                >
                  {{ searchResult.isFriend === 2 ? '再次发送请求' : '添加好友' }}
                </el-button>
                <el-tag v-else-if="searchResult.isFriend === 1" type="success" size="small">已是好友</el-tag>
                <el-tag v-else type="info" size="small">这是你自己</el-tag>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 空白状态 -->
      <div v-if="!selectedFriend && !searchResult && activeMenu === 'friends'" class="empty-chat">
        <div class="empty-chat-content">
          <el-icon :size="64" class="empty-icon">
            <ChatLineRound/>
          </el-icon>
          <p>选择一位好友开始聊天</p>
        </div>
      </div>

      <!-- 其他菜单项的内容 -->
      <div v-if="activeMenu === 'contacts'" class="contacts-container">
        <div v-loading="loading" class="contacts-list">
          <div class="contacts-header">
            <h3>我的好友列表</h3>
            <div class="contacts-search">
              <el-input
                  v-model="contactsSearchQuery"
                  placeholder="搜索好友"
                  clearable
                  size="small"
              >
                <template #prefix>
                  <el-icon>
                    <Search/>
                  </el-icon>
                </template>
              </el-input>
            </div>
          </div>

          <div class="contacts-content">
            <div
                v-for="friend in filteredContacts"
                :key="friend.id"
                class="contact-item"
            >
              <div class="contact-avatar">
                <el-avatar :size="50" :src="friend.avatar"/>
              </div>
              <div class="contact-info">
                <div class="contact-name">
                  {{ friend.remark || friend.nickname }}
                  <span class="contact-tfid">TF ID: {{ friend.tfId }}</span>
                </div>
                <div class="contact-email">{{ friend.email || '未设置邮箱' }}</div>
                <div class="contact-signature">{{ friend.signature || '这个人很懒，什么都没留下' }}</div>
              </div>
              <div class="contact-actions">
                <el-button type="primary" size="small" @click="selectFriend(friend)">发起聊天</el-button>
                <el-dropdown trigger="click" @command="handleFriendCommand($event, friend)">
                  <el-button size="small" icon="MoreFilled" circle plain></el-button>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item command="detail">查看资料</el-dropdown-item>
                      <el-dropdown-item command="remark">修改备注</el-dropdown-item>
                      <el-dropdown-item command="delete" divided>删除好友</el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </div>
            </div>

            <!-- 无好友时显示 -->
            <el-empty v-if="friendList.length === 0 && !loading" description="暂无好友">
              <el-button type="primary" size="small" @click="showSearchUserDialog = true">添加好友</el-button>
            </el-empty>
          </div>
        </div>
      </div>

      <div v-if="activeMenu !== 'friends' && activeMenu !== 'contacts'" class="placeholder-content">
        <el-empty :description="`${menuLabels[activeMenu] || '未知功能'}即将上线`"/>
      </div>
    </div>

    <!-- 个人资料弹窗 -->
    <el-dialog
        v-model="showProfile"
        title="我的个人资料"
        width="500px"
        destroy-on-close
        :close-on-click-modal="false"
    >
      <div v-loading="loading" class="profile-content">
        <div class="profile-avatar">
          <el-avatar :size="100" :src="userProfile.avatar"/>
          <div class="avatar-upload">
            <el-button size="small" type="primary" @click="uploadAvatar">更换头像</el-button>
          </div>
        </div>

        <el-form
            ref="profileFormRef"
            :model="userProfile"
            :rules="profileRules"
            label-width="90px"
        >
          <el-form-item label="TF ID" prop="tfId">
            <el-input v-model="userProfile.tfId" placeholder="请输入TF ID"/>
          </el-form-item>
          <el-form-item label="昵称" prop="nickname">
            <el-input v-model="userProfile.nickname" placeholder="请输入昵称" maxlength="20"/>
          </el-form-item>
          <el-form-item label="邮箱">
            <el-input v-model="userInfo.email" disabled/>
          </el-form-item>
          <el-form-item label="性别" prop="gender">
            <el-radio-group v-model="userProfile.gender">
              <el-radio :label="1">男</el-radio>
              <el-radio :label="2">女</el-radio>
              <el-radio :label="0">保密</el-radio>
            </el-radio-group>
          </el-form-item>
          <el-form-item label="搜索设置" prop="searchEnabled">
            <el-radio-group v-model="userProfile.searchEnabled">
              <el-radio :label="1">可被搜索</el-radio>
              <el-radio :label="0">不可被搜索</el-radio>
            </el-radio-group>
          </el-form-item>
          <el-form-item label="个性签名" prop="signature">
            <el-input
                v-model="userProfile.signature"
                type="textarea"
                :rows="3"
                placeholder="请输入个性签名"
                maxlength="50"
                show-word-limit
            />
          </el-form-item>
        </el-form>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="showProfile = false">取消</el-button>
          <el-button type="primary" :loading="loading" @click="saveProfile">保存</el-button>
        </span>
      </template>
    </el-dialog>

    <!-- 好友详情弹窗 -->
    <el-dialog
        v-model="showFriendDetail"
        title="好友详情"
        width="500px"
        destroy-on-close
        :close-on-click-modal="false"
    >
      <div v-loading="loadingFriendDetail" class="friend-detail-content">
        <div class="friend-detail-avatar">
          <el-avatar :size="100" :src="friendDetail?.avatar"/>
        </div>

        <div class="friend-detail-info">
          <div class="detail-item">
            <span class="label">TF ID:</span>
            <span class="value">{{ friendDetail?.tfId }}</span>
          </div>
          <div class="detail-item">
            <span class="label">昵称:</span>
            <span class="value">{{ friendDetail?.nickname }}</span>
          </div>
          <div class="detail-item">
            <span class="label">邮箱:</span>
            <span class="value">{{ friendDetail?.email }}</span>
          </div>
          <div class="detail-item">
            <span class="label">性别:</span>
            <span class="value">
              {{ friendDetail?.gender === 1 ? '男' : (friendDetail?.gender === 2 ? '女' : '保密') }}
            </span>
          </div>
          <div class="detail-item">
            <span class="label">备注:</span>
            <span class="value">{{ friendDetail?.remark || '无' }}</span>
          </div>
          <div class="detail-item">
            <span class="label">个性签名:</span>
            <span class="value">{{ friendDetail?.signature || '这个人很懒，什么都没留下' }}</span>
          </div>
        </div>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="showFriendDetail = false">关闭</el-button>
        </span>
      </template>
    </el-dialog>

    <!-- 添加好友弹窗 -->
    <el-dialog
        v-model="showSearchUserDialog"
        title="添加好友"
        width="500px"
        destroy-on-close
        :close-on-click-modal="false"
    >
      <div v-if="!searchResult" class="search-user-form">
        <p class="search-tip">通过TF ID或邮箱查找用户</p>
        <el-input
            v-model="searchQuery"
            placeholder="输入TF ID或邮箱"
            clearable
            class="search-input"
        >
          <template #prefix>
            <el-icon>
              <Search/>
            </el-icon>
          </template>
          <template #append>
            <el-button :loading="searchLoading" @click="handleSearch">
              <el-icon>
                <Search/>
              </el-icon>
            </el-button>
          </template>
        </el-input>
      </div>

      <div v-if="searchResult" class="search-result-container">
        <div class="search-result-user">
          <el-avatar :size="60" :src="searchResult.avatar"/>
          <div class="search-result-info">
            <div class="search-result-name">
              {{ searchResult.nickname }}
              <span class="search-result-tfid">TF ID: {{ searchResult.tfId }}</span>
            </div>
            <div class="search-result-email">{{ searchResult.email }}</div>
            <div class="search-result-signature">{{ searchResult.signature || '这个人很懒，什么都没留下' }}</div>
          </div>
        </div>

        <div class="search-result-actions">
          <el-button
              v-if="(!searchResult.isFriend || searchResult.isFriend === 2) && searchResult.id !== userInfo.id"
              type="primary"
              @click="handleAddFriend(searchResult)"
          >
            {{ searchResult.isFriend === 2 ? '再次发送请求' : '添加好友' }}
          </el-button>
          <el-tag v-else-if="searchResult.isFriend === 1" type="success">已是好友</el-tag>
          <el-tag v-else type="info">这是你自己</el-tag>
        </div>
      </div>

      <template #footer>
        <span class="dialog-footer">
          <el-button @click="closeSearchDialog">{{ searchResult ? '返回搜索' : '取消' }}</el-button>
        </span>
      </template>
    </el-dialog>

    <!-- 图片查看器 -->
    <el-image-viewer
        v-if="previewVisible"
        :url-list="[previewImageUrl]"
        :initial-index="0"
        :zoom-rate="1.2"
        :infinite="false"
        @close="closePreview"
    />

    <!-- 视频全屏查看器 (使用覆盖层而不是对话框) -->
    <div v-if="videoPreviewVisible" class="video-player-overlay" @click.self="closeVideoPreview">
      <div class="video-player-header">
        <span class="video-title">{{ videoPreviewTitle }}</span>
        <div class="video-actions">
          <el-button type="primary" size="small" @click="toggleFullScreen" class="fullscreen-btn">
            <el-icon>
              <FullScreen/>
            </el-icon>
            全屏
          </el-button>
          <el-icon class="close-icon" @click="closeVideoPreview">
            <Close/>
          </el-icon>
        </div>
      </div>
      <div class="video-player-container">
        <video
            ref="fullscreenVideoRef"
            :src="videoPreviewUrl"
            class="player-video"
            controls
            autoplay
            loop
            @click.stop
        >
          您的浏览器不支持视频播放
        </video>
      </div>
    </div>
  </div>
</template>

<script>
import {computed, onMounted, onUnmounted, reactive, ref, watch} from 'vue'
import {useRouter} from 'vue-router'
import {ElImageViewer, ElMessage, ElMessageBox} from 'element-plus'
import {
  ChatDotRound,
  ChatLineRound,
  Close,
  Delete,
  Document,
  DocumentDelete,
  Download,
  FullScreen,
  Loading,
  Microphone,
  MoreFilled,
  Paperclip,
  Phone,
  Picture,
  PictureFilled,
  Plus,
  Search,
  Service,
  Setting,
  SwitchButton,
  User,
  VideoCameraFilled
} from '@element-plus/icons-vue'
import {getUserInfo, updateProfile, uploadUserAvatar} from '@/api/user'
import {
  addFriend,
  deleteFriend,
  getFriendInfo,
  getFriendList,
  getFriendRequests,
  handleFriendRequest,
  searchFriend,
  updateFriendRemark
} from '@/api/friend'
import {addMessageListener, closeWebSocket, removeMessageListener} from '@/utils/websocket'
import {
  deleteChatHistory,
  getMessageList,
  getUnreadMessageCount,
  markMessagesAsRead,
  MessageType as ApiMessageType,
  sendMessageRequest
} from '@/api/message'
import PinyinMatch from 'pinyin-match'
import {getFileUrl, uploadFile} from '@/api/file'
import {registerVideo} from '@/utils/videoManager'
import audioRecorder from '@/utils/audioRecorder' // 导入已有的音频录制工具

// 添加MessageType枚举值
const MessageType = {
  ...ApiMessageType,
  VIDEO: 3,  // 添加视频类型
  AUDIO: 4   // 添加语音类型
}

export default {
  name: 'HomePage',
  components: {
    User, Setting, ChatDotRound, Service, SwitchButton,
    Search, Phone, VideoCameraFilled, MoreFilled, ChatLineRound,
    PictureFilled, Paperclip, Microphone, Loading, Plus, Document, Delete, Download,
    ElImageViewer,
    Picture, DocumentDelete, Close, FullScreen
  },
  setup() {
    const router = useRouter()
    const activeMenu = ref('friends')
    const showProfile = ref(false)
    const selectedFriend = ref(null)
    const searchQuery = ref('')
    const messageText = ref('')
    const loading = ref(true)
    const profileFormRef = ref(null)
    const showAddFriendDialog = ref(false)
    const searchResult = ref(null)
    const searchLoading = ref(false)
    const friendSearchQuery = ref('')
    const showSearchUserDialog = ref(false)
    const contactsSearchQuery = ref('') // 通讯录搜索关键词

    // 文件输入引用
    const imageInputRef = ref(null)
    const fileInputRef = ref(null)

    // 语音录制相关状态
    const isRecording = ref(false)
    const recordingDuration = ref(0)
    const recordingTimer = ref(null)
    const tempAudioFile = ref(null)

    // 分页参数
    const pagination = reactive({
      page: 1,
      size: 20,
      total: 0
    })

    // 用户信息
    const userInfo = reactive({
      id: '',
      email: '',
      tfId: '',
      nickname: '加载中...',
      avatar: 'https://avatars.githubusercontent.com/u/29829757',
      gender: 1,
      searchEnabled: 1,
      signature: ''
    })

    // 用于编辑的个人资料
    const userProfile = reactive({
      tfId: '',
      nickname: '',
      avatar: '',
      gender: 1,
      searchEnabled: 1,
      signature: '这个人很懒，什么都没留下'
    })

    // 好友列表
    const friendList = ref([])

    // 好友请求列表
    const friendRequests = ref([])
    const requestLoading = ref(false)

    // 消息记录
    const messageList = ref([])
    const messageLoading = ref(false)

    // 消息分页参数
    const messagePagination = reactive({
      page: 1,
      size: 20,
      hasMore: true
    })

    // 未读消息计数
    const unreadMessages = ref({})  // 格式: { userId: count }

    // 菜单标签映射
    const menuLabels = {
      friends: '我的好友',
      contacts: '通讯录',
      aiChat: 'AI助手'
    }

    // 好友请求数量
    const friendRequestCount = ref(0)

    // 好友详情数据
    const friendDetail = ref(null)
    const showFriendDetail = ref(false)
    const loadingFriendDetail = ref(false)

    // 存储已处理的通知消息，防止重复处理
    const processedNotifications = new Set();

    // 添加一个新的响应式变量存储临时选择的图片
    const tempSelectedImage = ref(null);

    // 添加图片预览相关的状态
    const previewVisible = ref(false);
    const previewImageUrl = ref('');

    // 新增临时文件的响应式变量
    const tempSelectedFile = ref(null);

    // 在响应式变量部分添加上传进度变量
    const uploadProgress = ref(0)
    const isUploading = ref(false)

    // 添加视频预览相关的状态
    const videoPreviewVisible = ref(false);
    // 添加图片错误处理相关的状态
    const imageErrorMessages = ref(new Set()) // 存储已显示错误的图片URL
    const videoPreviewUrl = ref('');
    const videoPreviewTitle = ref('');

    // 视频预览相关
    const fullscreenVideoRef = ref(null);

    // 获取好友列表
    const fetchFriends = async () => {
      try {
        loading.value = true
        const result = await getFriendList(pagination.page, pagination.size)
        console.log('获取到的好友列表:', result)

        // 分析好友列表中的userId字段
        if (result.records && result.records.length > 0) {
          console.log('第一个好友对象:', result.records[0])
          console.log('好友ID类型检查:')
          result.records.forEach((friend, index) => {
            console.log(`好友[${index}] - userId: ${friend.userId}, 类型: ${typeof friend.userId}`)
          })
        }

        friendList.value = result.records || []
        pagination.total = result.total || 0
      } catch (error) {
        console.error('获取好友列表失败:', error)
        // API层已经显示了错误消息
      } finally {
        loading.value = false
      }
    }

    // 过滤好友列表
    const filteredFriends = computed(() => {
      if (!friendSearchQuery.value) return friendList.value

      const query = friendSearchQuery.value.toLowerCase();

      return friendList.value.filter(friend => {
        // 直接匹配（区分大小写）
        const directMatch =
            friend.nickname.toLowerCase().includes(query) ||
            (friend.remark && friend.remark.toLowerCase().includes(query));

        // 使用PinyinMatch库进行拼音匹配
        const pinyinMatch =
            PinyinMatch.match(friend.nickname, query) ||
            (friend.remark && PinyinMatch.match(friend.remark, query));

        return directMatch || pinyinMatch;
      });
    })

    // 通讯录好友过滤
    const filteredContacts = computed(() => {
      if (!contactsSearchQuery.value) return friendList.value

      const query = contactsSearchQuery.value.toLowerCase();

      return friendList.value.filter(friend => {
        // 直接匹配（区分大小写）
        const directMatch =
            friend.nickname.toLowerCase().includes(query) ||
            (friend.remark && friend.remark.toLowerCase().includes(query)) ||
            (friend.email && friend.email.toLowerCase().includes(query)) ||
            (friend.tfId && friend.tfId.toLowerCase().includes(query));

        // 使用PinyinMatch库进行拼音匹配
        const pinyinMatch =
            PinyinMatch.match(friend.nickname, query) ||
            (friend.remark && PinyinMatch.match(friend.remark, query));

        return directMatch || pinyinMatch;
      });
    })

    // 处理菜单选择
    const handleMenuSelect = (index) => {
      activeMenu.value = index
      if (index !== 'friends') {
        selectedFriend.value = null
      }

      // 导航到AI聊天页面
      if (index === 'aiChat') {
        router.push('/ai-chat')
      }
    }

    // 选择好友进行聊天
    const selectFriend = async (friend) => {
      console.log('选择好友:', friend)
      selectedFriend.value = friend
      // 重置分页数据
      messagePagination.page = 1
      messagePagination.hasMore = true
      // 加载消息记录
      fetchMessages(friend.userId, true)

      // 标记消息为已读并清除未读消息计数
      if (unreadMessages.value[friend.userId] && unreadMessages.value[friend.userId] > 0) {
        console.log(`标记好友[${friend.nickname}](ID:${friend.userId})的未读消息为已读, 未读数量:`, unreadMessages.value[friend.userId])
        try {
          await markMessagesAsRead(friend.userId)
          // 清除本地未读计数
          unreadMessages.value[friend.userId] = 0
          console.log('已清除本地未读计数')
        } catch (error) {
          console.error('标记消息为已读失败', error)
        }
      } else {
        console.log(`好友[${friend.nickname}](ID:${friend.userId})没有未读消息`)
      }
    }

    // 获取消息记录
    const fetchMessages = async (friendUserId, isRefresh = false) => {
      try {
        // 判断是否还有更多消息
        if (!messagePagination.hasMore && !isRefresh) {
          return
        }

        messageLoading.value = true

        // 如果是刷新，清空现有消息
        if (isRefresh) {
          messageList.value = []
          messagePagination.page = 1
        }

        const res = await getMessageList(friendUserId, messagePagination.page, messagePagination.size)
        console.log('获取到的消息记录:', res)

        // 处理获取到的消息记录
        let newMessages = []

        if (res && res.records && Array.isArray(res.records)) {
          // 将消息按时间顺序排序（从旧到新）
          newMessages = [...res.records].sort((a, b) => {
            return new Date(a.createTime) - new Date(b.createTime)
          })

          // 判断是否还有更多消息
          messagePagination.hasMore = res.records.length >= messagePagination.size
        } else if (res && Array.isArray(res)) {
          // 如果直接返回数组
          newMessages = [...res].sort((a, b) => {
            return new Date(a.createTime) - new Date(b.createTime)
          })

          // 判断是否还有更多消息
          messagePagination.hasMore = res.length >= messagePagination.size
        }

        // 确保每条消息都有status字段
        newMessages = newMessages.map(msg => {
          // 如果status不存在，则根据是否有readTime字段来判断已读状态
          if (typeof msg.status === 'undefined') {
            return {
              ...msg,
              status: msg.readTime ? 1 : 0
            }
          }
          return msg
        })

        // 保存旧消息滚动位置
        const chatMessagesEl = document.querySelector('.chat-messages')
        const oldScrollHeight = chatMessagesEl?.scrollHeight || 0
        const oldScrollTop = chatMessagesEl?.scrollTop || 0

        // 添加新消息
        if (isRefresh) {
          messageList.value = newMessages
          // 刷新时滚动到底部
          scrollToBottom()
        } else {
          // 加载更多历史消息时，添加到列表前面
          messageList.value = [...newMessages, ...messageList.value]

          // 恢复原来的滚动位置
          if (chatMessagesEl) {
            setTimeout(() => {
              const newScrollHeight = chatMessagesEl.scrollHeight
              chatMessagesEl.scrollTop = oldScrollTop + (newScrollHeight - oldScrollHeight)
            }, 50)
          }
        }

        // 加载完成后，如果是第一页，滚动到底部
        if (isRefresh) {
          scrollToBottom()
        }

        // 更新页码
        if (!isRefresh && messagePagination.hasMore) {
          messagePagination.page++
        }
      } catch (error) {
        ElMessage.error('获取消息记录失败')
        if (isRefresh) {
          messageList.value = []
        }
      } finally {
        messageLoading.value = false
      }
    }

    // 监听聊天窗口滚动，当滚动到顶部时加载更多消息
    const handleChatScroll = (event) => {
      const {scrollTop} = event.target
      // 当滚动到距离顶部50px以内，且不是正在加载中，且有更多消息时，加载更多
      if (scrollTop < 50 && !messageLoading.value && messagePagination.hasMore && selectedFriend.value) {
        fetchMessages(selectedFriend.value.userId)
      }
    }

    // 滚动到消息底部
    const scrollToBottom = () => {
      setTimeout(() => {
        const chatMessagesEl = document.querySelector('.chat-messages')
        if (chatMessagesEl) {
          chatMessagesEl.scrollTop = chatMessagesEl.scrollHeight
        }
      }, 100)
    }

    // 发送消息
    const sendMessage = async () => {
      if (!selectedFriend.value) {
        ElMessage.warning('请先选择一个聊天对象');
        return;
      }

      // 如果有临时图片，发送图片消息
      if (tempSelectedImage.value) {
        try {
          const tempId = 'temp-' + Date.now();

          // 显示上传进度
          ElMessage.info({
            message: '正在上传图片...',
            duration: 0,
            showClose: true
          });

          // 上传图片到服务器
          const response = await uploadFile(tempSelectedImage.value.file);
          console.log('图片上传成功:', response);

          // 关闭上传提示
          ElMessage.closeAll();

          if (!response) {
            throw new Error('上传响应格式不正确');
          }

          // 获取文件ID和路径 - response已经是data部分的内容
          const {id: fileId, path: filePath} = response;

          // 创建临时消息对象显示在UI上（直接使用path作为图片URL）
          const tempMessage = {
            id: tempId,
            senderUserId: userInfo.id,
            receiverUserId: selectedFriend.value.userId,
            content: filePath, // 使用path作为content
            fileId: fileId,
            filePath: filePath, // 保存文件路径用于直接展示
            msgType: MessageType.IMAGE,
            createTime: new Date().toISOString(),
            status: 0 // 未读状态
          };

          // 添加到消息列表，立即显示
          messageList.value.push(tempMessage);

          // 滚动到底部
          scrollToBottom();

          // 发送图片消息
          const messageData = {
            receiverUserId: selectedFriend.value.userId,
            content: filePath,
            msgType: MessageType.IMAGE,
            fileId: fileId, // 添加文件ID字段
            filePath: filePath // 添加文件路径字段
          };

          // 发送消息到服务器
          const result = await sendMessageRequest(messageData);

          // 如果需要，可以根据返回结果更新临时消息
          if (result && result.id) {
            const index = messageList.value.findIndex(m => m.id === tempId);
            if (index !== -1) {
              // 更新消息，保留未读状态
              messageList.value[index] = {
                ...result,
                status: result.status !== undefined ? result.status : 0
              };
            }
          }

          // 清空临时图片
          tempSelectedImage.value = null;

        } catch (error) {
          console.error('发送图片失败:', error);
          ElMessage.error('发送图片失败: ' + (error.message || '未知错误'));

          // 清空临时图片
          tempSelectedImage.value = null;
        }
        return;
      }

      // 如果有临时文件，发送文件消息
      if (tempSelectedFile.value) {
        try {
          const tempId = 'temp-' + Date.now();
          isUploading.value = true;
          uploadProgress.value = 0;

          // 获取文件类型，默认为FILE
          const msgType = tempSelectedFile.value.type || MessageType.FILE;

          // 创建临时消息对象显示在UI上（带上传进度）
          const tempMessage = {
            id: tempId,
            senderUserId: userInfo.id,
            receiverUserId: selectedFriend.value.userId,
            content: '',
            fileName: tempSelectedFile.value.file.name,
            fileSize: tempSelectedFile.value.file.size,
            msgType: msgType, // 使用确定的消息类型
            createTime: new Date().toISOString(),
            status: 0, // 未读状态
            isUploading: true, // 标记为正在上传
            uploadProgress: 0 // 上传进度
          };

          // 添加到消息列表，立即显示
          messageList.value.push(tempMessage);

          // 滚动到底部
          scrollToBottom();

          // 显示上传进度提示
          const loadingInstance = ElMessage({
            type: 'info',
            message: `正在上传${msgType === MessageType.VIDEO ? '视频' : '文件'} ${tempSelectedFile.value.file.name}...`,
            duration: 0,
            showClose: true
          });

          // 上传文件到服务器，传入进度回调和类型信息
          const response = await uploadFile(tempSelectedFile.value.file, {
            onProgress: (progress) => {
              // 更新上传进度
              uploadProgress.value = progress;
              // 更新消息列表中的进度显示
              const index = messageList.value.findIndex(m => m.id === tempId);
              if (index !== -1) {
                messageList.value[index].uploadProgress = progress;
              }
            },
            extraData: {
              msgType: msgType // 将消息类型传递给上传函数
            }
          });

          // 关闭上传提示
          loadingInstance.close();
          ElMessage.closeAll();

          if (!response) {
            throw new Error('上传响应格式不正确');
          }

          // 获取文件ID
          const {id: fileId, path: filePath} = response;

          // 更新临时消息
          const messageIndex = messageList.value.findIndex(m => m.id === tempId);
          if (messageIndex !== -1) {
            // 只更新消息ID和上传状态，保留其他信息
            messageList.value[messageIndex] = {
              ...messageList.value[messageIndex],
              id: response.messageId || 'msg-' + Date.now(), // 使用后端返回的消息ID或生成一个
              fileId: fileId,
              filePath: filePath,
              content: filePath,
              isUploading: false, // 取消上传状态
              uploadProgress: 100
            };
          }

          // 在清空临时文件前保存必要的信息
          const fileName = tempSelectedFile.value.file.name;
          const fileSize = tempSelectedFile.value.file.size;

          // 清空临时文件
          tempSelectedFile.value = null;
          isUploading.value = false;

          // 发送消息
          const msgResult = await sendMessageRequest({
            receiverUserId: selectedFriend.value.userId,
            content: filePath,
            msgType: msgType,
            fileId: fileId,
            filePath: filePath,
            fileName: fileName,
            fileSize: fileSize
          });

          // 如果需要，可以根据返回结果更新临时消息
          if (msgResult && msgResult.id) {
            const index = messageList.value.findIndex(m => m.id === tempId);
            if (index !== -1) {
              // 更新消息，保留未读状态
              messageList.value[index] = {
                ...msgResult,
                status: msgResult.status !== undefined ? msgResult.status : 0
              };
            }
          }

          // 根据消息类型显示不同的成功提示
          if (msgType === MessageType.VIDEO) {
            ElMessage.success('视频发送成功');
          } else {
            ElMessage.success('文件发送成功');
          }
        } catch (error) {
          console.error('发送文件消息失败:', error);
          ElMessage.error('文件发送失败: ' + (error.message || '未知错误'));

          // 重置上传状态
          isUploading.value = false;
          uploadProgress.value = 0;
          tempSelectedFile.value = null;
        }
        return;
      }

      // 处理文本消息的现有逻辑
      if (!messageText.value || !messageText.value.trim()) return;

      try {
        const messageData = {
          receiverUserId: selectedFriend.value.userId,
          content: messageText.value.trim(),
          msgType: MessageType.TEXT
        }

        // 先清空输入框，提升用户体验
        const tempMessage = messageText.value.trim()
        messageText.value = ''

        // 创建一个临时消息对象，用于立即显示在UI上
        const tempMessageObj = {
          id: 'temp-' + Date.now(),
          senderUserId: userInfo.id,
          receiverUserId: selectedFriend.value.userId,
          content: tempMessage,
          msgType: MessageType.TEXT,
          createTime: new Date().toISOString(),
          status: 0  // 0表示未读状态
        }

        // 添加临时消息到列表，立即显示
        messageList.value.push(tempMessageObj)

        // 滚动到底部
        scrollToBottom()

        try {
          // 发送消息到服务器
          const result = await sendMessageRequest(messageData)

          // 如果需要，可以根据返回结果更新临时消息
          if (result && result.id) {
            const index = messageList.value.findIndex(m => m.id === tempMessageObj.id)
            if (index !== -1) {
              // 确保保留未读状态
              messageList.value[index] = {
                ...result,
                status: result.status !== undefined ? result.status : 0  // 如果服务器没有返回status，则默认为未读
              }
            }
          }
        } catch (sendError) {
          // 移除临时消息
          messageList.value = messageList.value.filter(m => m.id !== tempMessageObj.id)

          // 恢复消息文本
          messageText.value = tempMessage

          // 显示更具体的错误信息
          if (sendError.response && sendError.response.status === 302) {
            ElMessage.error('发送失败：请求被重定向，请检查API配置')
          } else {
            ElMessage.error('发送消息失败，请重试')
          }
        }
      } catch {
        ElMessage.error('发送消息失败')
      }
    }

    // 获取最新的用户信息
    const fetchUserInfo = async () => {
      try {
        loading.value = true
        const res = await getUserInfo()

        // 更新用户信息
        Object.assign(userInfo, res)

        // 更新编辑资料
        userProfile.tfId = res.tfId
        userProfile.nickname = res.nickname
        userProfile.avatar = res.avatar
        userProfile.gender = res.gender !== undefined ? res.gender : 1
        userProfile.searchEnabled = res.searchEnabled !== undefined ? res.searchEnabled : 1
        userProfile.signature = res.signature || '这个人很懒，什么都没留下'
      } catch {
        // API层已经显示了错误消息，这里不需要再次显示
      } finally {
        loading.value = false
      }
    }

    // 获取好友请求列表
    const fetchFriendRequests = async () => {
      try {
        requestLoading.value = true
        const res = await getFriendRequests()
        console.log('获取好友请求列表详情:', res)

        if (res && Array.isArray(res)) {
          friendRequests.value = res
        } else if (res && res.code === 1 && res.data) {
          friendRequests.value = res.data || []
        } else {
          friendRequests.value = []
        }
      } catch (error) {
        console.error('获取好友请求列表失败:', error)
        // API层已经显示了错误消息
        friendRequests.value = []
      } finally {
        requestLoading.value = false
      }
    }

    // 处理好友请求
    const handleFriendRequestAction = async (request, action) => {
      try {
        await handleFriendRequest(request.id, action)
        ElMessage.success(action === 'accept' ? '已接受好友请求' : '已拒绝好友请求')
        // 重新获取好友请求列表
        await fetchFriendRequests()
        // 如果是接受请求，刷新好友列表
        if (action === 'accept') {
          await fetchFriends()
        }
      } catch (error) {
        console.error('处理好友请求失败:', error)
        // API层已经显示了错误消息
      }
    }

    // 获取好友请求数量
    const fetchFriendRequestCount = () => {
      getFriendRequests()
          .then(res => {
            // 处理各种格式的响应
            if (res && Array.isArray(res)) {
              friendRequestCount.value = res.length
            } else if (res && res.code === 1 && res.data && Array.isArray(res.data)) {
              // 处理响应格式为 { code: 1, data: [...] } 的情况
              friendRequestCount.value = res.data.length
            } else if (res && res.data && Array.isArray(res.data)) {
              // 处理其他格式的响应，data字段直接是数组
              friendRequestCount.value = res.data.length
            } else {
              friendRequestCount.value = 0
            }
          })
          .catch(() => {
            friendRequestCount.value = 0
          })
    }

    // 获取未读消息数
    const fetchUnreadMessageCount = async () => {
      try {
        const result = await getUnreadMessageCount()

        if (result) {
          // 处理返回格式: { unreadBySender: { "7": "20" }, totalUnread: "20" }
          const {unreadBySender} = result

          // 清空当前未读消息计数
          unreadMessages.value = {}

          // 将每个发送者的未读消息数添加到响应式对象中
          // 注意：后端返回的是字符串数字，需要转换为数字
          if (unreadBySender) {
            Object.keys(unreadBySender).forEach(senderId => {
              unreadMessages.value[senderId] = parseInt(unreadBySender[senderId]) || 0
            })
          }
        }
      } catch (error) {
        // 静默处理错误
      }
    }

    // 组件挂载时获取用户信息和好友列表
    onMounted(async () => {
      // 确保先获取用户信息和好友列表
      await fetchUserInfo();
      await fetchFriends();
      await fetchFriendRequestCount();
      await fetchUnreadMessageCount();  // 获取未读消息数

      // 添加WebSocket消息监听
      addMessageListener(handleWebSocketMessage);

      // 添加页面关闭前的事件监听，确保正确关闭WebSocket连接
      window.addEventListener('beforeunload', handleBeforeUnload);

      // 添加视频管理，确保同时只播放一个视频
      setTimeout(() => {
        const videos = document.querySelectorAll('.message-video');
        videos.forEach(video => {
          registerVideo(video);
        });
      }, 1000);

      // 检查URL参数，如果有selectFriend参数，自动选择对应的好友
      const friendIdToSelect = router.currentRoute.value.query.selectFriend;
      if (friendIdToSelect) {
        // 等待好友列表加载完成
        const checkFriendInterval = setInterval(() => {
          if (friendList.value.length > 0) {
            const friendToSelect = friendList.value.find(f => f.userId === friendIdToSelect);
            if (friendToSelect) {
              // 调用选择好友函数
              selectFriend(friendToSelect);
              // 清除URL参数
              router.replace({path: '/home', query: {}});
            }
            clearInterval(checkFriendInterval);
          }
        }, 200);

        // 设置超时，避免无限检查
        setTimeout(() => {
          clearInterval(checkFriendInterval);
        }, 5000);
      }
    });

    // 组件卸载时清除监听器
    onUnmounted(() => {
      removeMessageListener(handleWebSocketMessage);

      // 移除页面关闭事件监听
      window.removeEventListener('beforeunload', handleBeforeUnload);

      // 主动关闭WebSocket连接
      closeWebSocket();
    })

    // 页面关闭前的处理函数
    const handleBeforeUnload = () => {
      // 在页面关闭前主动关闭WebSocket连接
      closeWebSocket();
    };

    // 个人资料表单验证规则
    const profileRules = {
      tfId: [
        {required: true, message: '请输入TF ID', trigger: 'blur'}
      ],
      nickname: [
        {required: true, message: '请输入昵称', trigger: 'blur'},
        {min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur'}
      ],
      gender: [
        {required: true, message: '请选择性别', trigger: 'change'}
      ],
      searchEnabled: [
        {required: true, message: '请选择搜索设置', trigger: 'change'}
      ],
      signature: [
        {max: 50, message: '个性签名不能超过50个字符', trigger: 'blur'}
      ]
    }

    // 保存个人资料
    const saveProfile = async () => {
      if (!profileFormRef.value) return

      try {
        // 表单验证
        await profileFormRef.value.validate()

        loading.value = true

        // 构建要更新的数据
        const profileData = {
          tfId: userProfile.tfId,
          nickname: userProfile.nickname,
          avatar: userProfile.avatar,
          gender: userProfile.gender,
          searchEnabled: userProfile.searchEnabled,
          signature: userProfile.signature
        }

        // 发送更新请求
        await updateProfile(profileData)

        // 更新成功后重新获取最新数据
        await fetchUserInfo()

        ElMessage.success('个人资料保存成功')
        showProfile.value = false
      } catch {
        // API层已经显示了错误消息，这里不需要再次显示
      } finally {
        loading.value = false
      }
    }

    // 退出登录
    const handleLogout = () => {
      ElMessageBox.confirm(
          '确定要退出登录吗？',
          '提示',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }
      ).then(() => {
        // 关闭WebSocket连接
        closeWebSocket()

        // 清除登录信息
        localStorage.removeItem('token')
        localStorage.removeItem('userId')

        // 跳转到登录页
        router.push('/login')
        ElMessage.success('已退出登录')
      }).catch(() => {
      })
    }

    // 加载更多好友
    const loadMoreFriends = async () => {
      if (friendList.value.length >= pagination.total || loading.value) return

      pagination.page++
      try {
        loading.value = true
        const result = await getFriendList(pagination.page, pagination.size)
        if (result.records && result.records.length > 0) {
          friendList.value = [...friendList.value, ...result.records]
        }
      } catch (error) {
        pagination.page-- // 恢复页码
      } finally {
        loading.value = false
      }
    }

    // 搜索好友
    const handleSearch = async () => {
      if (!searchQuery.value.trim()) {
        searchResult.value = null
        return
      }

      try {
        searchLoading.value = true
        const result = await searchFriend(searchQuery.value.trim())
        searchResult.value = result
      } catch (error) {
        // API层已经显示了错误消息
      } finally {
        searchLoading.value = false
      }
    }

    // 关闭搜索对话框
    const closeSearchDialog = () => {
      if (searchResult.value) {
        // 如果已有搜索结果，则返回搜索界面
        searchResult.value = null
      } else {
        // 否则关闭对话框
        showSearchUserDialog.value = false
      }
      // 清空搜索词
      searchQuery.value = ''
    }

    // 添加好友
    const handleAddFriend = async (user) => {
      try {
        const {value: remark} = await ElMessageBox.prompt('请输入好友备注（可选）', '添加好友', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          inputPattern: /^.{0,20}$/,
          inputErrorMessage: '备注长度不能超过20个字符'
        })

        try {
          await addFriend(user.id, remark || '')
          ElMessage.success('好友请求发送成功')
          // 重新获取好友列表
          await fetchFriends()
        } catch (error) {
          if (error.response && error.response.data) {
            const {msg} = error.response.data
            ElMessage.warning(msg || '添加好友失败')
          }
        }
      } catch (error) {
        if (error !== 'cancel') {
          // API层已经显示了错误消息
        }
      }
    }

    // 查看好友请求
    const viewFriendRequests = () => {
      router.push('/friend-requests')
    }

    // 查看好友详情
    const viewFriendDetail = (friend) => {
      friendDetail.value = null
      showFriendDetail.value = true
      loadingFriendDetail.value = true

      getFriendInfo(friend.userId)
          .then(res => {
            friendDetail.value = res
          })
          .catch(() => {
            // API层已经显示了错误消息
          })
          .finally(() => {
            loadingFriendDetail.value = false
          })
    }

    // 处理好友操作命令
    const handleFriendCommand = (command, friend) => {
      switch (command) {
        case 'detail':
          viewFriendDetail(friend)
          break
        case 'remark':
          handleUpdateRemark(friend)
          break
        case 'delete':
          confirmDeleteFriend(friend)
          break
      }
    }

    // 修改好友备注
    const handleUpdateRemark = (friend) => {
      ElMessageBox.prompt(
          '请输入新的好友备注',
          '修改备注',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            inputValue: friend.remark || '',
            inputPattern: /^.{0,20}$/,
            inputErrorMessage: '备注长度不能超过20个字符'
          }
      ).then(({value}) => {
        updateFriendRemark(friend.userId, value)
            .then(() => {
              ElMessage.success('修改备注成功')
              // 更新本地数据
              const index = friendList.value.findIndex(item => item.userId === friend.userId)
              if (index !== -1) {
                friendList.value[index].remark = value
                // 如果是当前选中的好友，也更新选中的好友
                if (selectedFriend.value && selectedFriend.value.userId === friend.userId) {
                  selectedFriend.value.remark = value
                }
              }
            })
            .catch(() => {
              // API层已经显示了错误消息
            })
      }).catch(() => {
      })
    }

    // 确认删除好友
    const confirmDeleteFriend = (friend) => {
      ElMessageBox.confirm(
          `确定要删除好友"${friend.remark || friend.nickname}"吗？`,
          '删除好友',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }
      ).then(() => {
        deleteFriend(friend.userId)
            .then(() => {
              ElMessage.success('删除好友成功')
              // 从列表中移除
              friendList.value = friendList.value.filter(item => item.userId !== friend.userId)
              // 如果当前选中的是被删除的好友，清除选中
              if (selectedFriend.value && selectedFriend.value.userId === friend.userId) {
                selectedFriend.value = null
              }
            })
            .catch(() => {
              // API层已经显示了错误消息
            })
      }).catch(() => {
      })
    }

    // 上传头像
    const uploadAvatar = () => {
      // 创建文件选择输入框
      const fileInput = document.createElement('input')
      fileInput.type = 'file'
      fileInput.accept = 'image/*' // 只接受图片文件

      // 监听文件选择变化
      fileInput.onchange = async (event) => {
        const file = event.target.files[0]
        if (!file) return

        // 验证文件类型
        if (!file.type.startsWith('image/')) {
          ElMessage.warning('请选择图片文件')
          return
        }

        // 验证文件大小（限制为2MB）
        if (file.size > 2 * 1024 * 1024) {
          ElMessage.warning('图片大小不能超过2MB')
          return
        }

        try {
          // 先创建临时预览
          const reader = new FileReader()

          // 使用Promise包装FileReader的异步操作
          const previewUrl = await new Promise((resolve, reject) => {
            reader.onload = (e) => resolve(e.target.result)
            reader.onerror = (e) => reject(e)
            reader.readAsDataURL(file)
          })

          // 先更新显示（本地预览）
          userProfile.avatar = previewUrl // 表单里的头像
          userInfo.avatar = previewUrl    // 确保顶部头像也同步更新

          // 显示上传进度
          const loadingInstance = ElMessage({
            type: 'info',
            message: '正在上传头像...',
            duration: 0
          })

          // 上传到服务器
          const response = await uploadUserAvatar(file)

          // 关闭加载提示
          loadingInstance.close()

          // 处理不同格式的响应，获取头像URL
          let avatarUrl
          if (typeof response === 'string') {
            avatarUrl = response
          } else if (typeof response === 'object') {
            if (response.code === 1 && response.data) {
              avatarUrl = response.data
            } else if (response.url) {
              avatarUrl = response.url
            } else {
              throw new Error('返回的头像URL格式不正确')
            }
          } else {
            throw new Error('未能识别的服务器响应')
          }

          // 更新实际的服务器返回头像URL
          userProfile.avatar = avatarUrl // 表单里的头像
          userInfo.avatar = avatarUrl    // 确保顶部头像也同步更新

          ElMessage.success('头像上传成功')
        } catch (error) {
          console.error('头像上传失败:', error)
          ElMessage.error('头像上传失败，请重试')
        }
      }

      // 触发文件选择对话框
      fileInput.click()
    }

    // 处理接收到的WebSocket消息
    const handleWebSocketMessage = (data) => {
      console.log('收到WebSocket消息:', data)
      try {
        if (!data) {
          return
        }

        // 处理字符串类型的特殊通知消息
        if (typeof data === 'string') {
          // 检查消息是否已处理过
          if (processedNotifications.has(data)) {
            console.log(`消息 ${data} 已处理过，忽略`);
            return;
          }

          // 添加到已处理集合
          processedNotifications.add(data);

          // 设置一个短暂的过期时间，避免同一类型的通知在短时间内重复显示
          // 例如，5秒内不重复显示同类型的通知
          setTimeout(() => {
            processedNotifications.delete(data);
          }, 5000);

          // 检查是否是消息已读状态通知
          if (data === 'READ_ACK') {
            console.log('收到消息已读通知，准备更新消息状态')

            // 由于data就是一个字符串，需要更新所有发送的消息为已读
            if (selectedFriend.value && messageList.value.length > 0) {
              let updatedCount = 0

              // 找出所有由当前用户发送给当前选中好友的消息
              const messageIndicesToUpdate = []
              messageList.value.forEach((msg, index) => {
                if (msg.senderUserId == userInfo.id &&
                    msg.receiverUserId == selectedFriend.value.userId &&
                    msg.status !== 1) {
                  messageIndicesToUpdate.push(index)
                }
              })

              // 更新这些消息的状态
              messageIndicesToUpdate.forEach(index => {
                const updatedMessage = {...messageList.value[index], status: 1}
                messageList.value.splice(index, 1, updatedMessage)
                updatedCount++
              })

              console.log(`成功更新了${updatedCount}条消息的已读状态`)
            } else {
              console.warn('收到READ_ACK消息，但当前没有选择好友或没有消息')
            }

            return
          }

          // 检查是否是新好友请求通知
          if (data === 'NEW_FRIEND') {
            console.log('收到新好友请求通知，更新好友请求数量')

            // 获取最新的好友请求列表
            fetchFriendRequestCount()

            // 显示通知
            ElMessage({
              message: '您收到了新的好友请求',
              type: 'success',
              duration: 3000
            })

            return
          }

          // 检查是否是好友接受请求通知
          if (data === 'ACCEPT_FRIEND') {
            console.log('收到好友接受请求通知，刷新好友列表')

            // 刷新好友列表
            fetchFriends()

            // 显示通知
            ElMessage({
              message: '有用户接受了您的好友请求',
              type: 'success',
              duration: 3000
            })

            return
          }

          // 检查是否是好友删除通知
          if (data === 'DELETE_FRIEND') {
            console.log('收到好友删除通知，静默刷新好友列表')

            // 保存当前选中的好友ID
            const currentFriendId = selectedFriend.value?.userId

            // 静默刷新好友列表
            fetchFriends().then(() => {
              // 检查当前选中的好友是否还存在
              if (currentFriendId && !friendList.value.find(f => f.userId === currentFriendId)) {
                // 如果选中的好友已被删除，清空选中状态
                selectedFriend.value = null
                // 不显示任何提示，默默地返回到空聊天状态
              }
            })

            return
          }

          // 检查是否是好友邀请通知
          if (data === 'INVITE_FRIEND') {
            console.log('收到好友邀请通知，刷新好友邀请数量')

            // 获取最新的好友请求列表
            fetchFriendRequestCount()

            // 显示通知
            ElMessage({
              message: '您收到了新的好友邀请',
              type: 'success',
              duration: 3000
            })

            return
          }
        }

        // 处理对象类型的消息（普通聊天消息等）
        // 强制转换ID为字符串，确保比较一致性
        const msgId = data.id ? String(data.id) : null
        const senderId = data.senderUserId ? String(data.senderUserId) : null

        if (!msgId || !senderId) {
          console.warn('消息缺少必要字段id或senderUserId:', data)
          return
        }

        // 检查消息是否已处理过
        const messageUniqueId = `msg_${msgId}_${senderId}`;
        if (processedNotifications.has(messageUniqueId)) {
          console.log(`聊天消息 ${messageUniqueId} 已处理过，忽略`);
          return;
        }

        // 添加到已处理集合
        processedNotifications.add(messageUniqueId);

        // 设置一个短暂的过期时间，避免短时间内重复显示
        setTimeout(() => {
          processedNotifications.delete(messageUniqueId);
        }, 5000);

        console.log('发送者ID:', senderId, '类型:', typeof senderId)
        console.log('当前未读消息对象:', unreadMessages.value)

        // 判断是否正在与发送者聊天
        const isChatting = selectedFriend.value && String(selectedFriend.value.userId) === senderId

        // 处理各种类型的消息（文本、图片等）
        // 如果当前正在与发送者聊天，直接添加消息到列表并标记为已读
        if (isChatting) {
          console.log('正在与发送者聊天，标记为已读')
          // 检查消息是否已存在(避免重复)
          const existingMsgIndex = messageList.value.findIndex(m => String(m.id) === msgId)
          if (existingMsgIndex === -1) {
            // 根据消息类型处理
            const messageObj = {
              ...data,
              // 确保图片消息和其他类型消息都有正确的字段
              msgType: data.msgType ? Number(data.msgType) : MessageType.TEXT,
              content: data.content || '',
              status: 0 // 默认为未读状态
            }

            // 使用解构拷贝确保Vue响应式更新
            const newMessageList = [...messageList.value]
            newMessageList.push(messageObj)
            messageList.value = newMessageList

            // 滚动到底部
            scrollToBottom()

            // 自动标记为已读
            markMessagesAsRead(senderId).catch(error => {
              console.error('标记消息为已读失败', error)
            })
          }
        } else {
          console.log('不是当前聊天对象，增加未读计数')
          // 如果不是当前聊天好友，增加未读计数
          if (!unreadMessages.value[senderId]) {
            unreadMessages.value[senderId] = 0
          }
          unreadMessages.value[senderId]++

          console.log('更新后的未读消息:', unreadMessages.value)

          // 提示新消息 (只有在不是当前聊天对象时才显示通知)
          const sender = friendList.value.find(f => String(f.userId) === senderId)
          if (sender) {
            console.log('找到发送者:', sender.nickname)
            // 根据消息类型显示不同的提示
            const msgType = data.msgType ? parseInt(data.msgType) : MessageType.TEXT
            const msgTypeText = msgType === MessageType.IMAGE ? '图片' :
                msgType === MessageType.FILE ? '文件' :
                    msgType === MessageType.VOICE ? '语音' :
                        msgType === MessageType.VIDEO ? '视频' : '消息'

            ElMessage({
              message: `收到来自 ${sender.remark || sender.nickname} 的新${msgTypeText}`,
              type: 'info',
              duration: 3000
            })
          } else {
            console.log('未找到发送者，当前好友列表:', friendList.value)
          }
        }
      } catch (error) {
        console.error('处理WebSocket消息失败', error)
      }
    }

    // 格式化日期
    const formatDate = (dateString) => {
      if (!dateString) return '';
      try {
        const date = new Date(dateString);
        // 判断是否是今天
        const today = new Date();
        const isToday = date.getDate() === today.getDate() &&
            date.getMonth() === today.getMonth() &&
            date.getFullYear() === today.getFullYear();

        if (isToday) {
          // 如果是今天，只显示时间
          return date.toLocaleTimeString('zh-CN', {hour: '2-digit', minute: '2-digit'});
        } else {
          // 否则显示日期和时间
          return date.toLocaleString('zh-CN', {
            month: 'numeric',
            day: 'numeric',
            hour: '2-digit',
            minute: '2-digit'
          });
        }
      } catch {
        return dateString;
      }
    }

    // 手动加载更多消息
    const loadMoreMessages = () => {
      if (selectedFriend.value && !messageLoading.value && messagePagination.hasMore) {
        fetchMessages(selectedFriend.value.userId)
      }
    }

    // 处理聊天工具栏命令
    const handleChatCommand = (command) => {
      if (!selectedFriend.value) return;

      switch (command) {
        case 'clearChat':
          confirmClearChat();
          break;
        case 'viewProfile':
          viewFriendDetail(selectedFriend.value);
          break;
      }
    }

    // 确认清空聊天记录
    const confirmClearChat = () => {
      if (!selectedFriend.value) return;

      ElMessageBox.confirm(
          `确定要清空与"${selectedFriend.value.remark || selectedFriend.value.nickname}"的聊天记录吗？此操作不可恢复。`,
          '清空聊天记录',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }
      ).then(() => {
        clearChatHistory();
      }).catch(() => {
      });
    }

    // 清空聊天记录
    const clearChatHistory = async () => {
      if (!selectedFriend.value) return;

      try {
        // 显示加载状态
        messageLoading.value = true;

        // 调用删除API
        await deleteChatHistory(selectedFriend.value.userId);

        // 清空本地消息列表
        messageList.value = [];

        // 重置分页
        messagePagination.page = 1;
        messagePagination.hasMore = false;

        ElMessage.success('聊天记录已清空');
      } catch (error) {
        console.error('清空聊天记录失败:', error);
        ElMessage.error('清空聊天记录失败，请重试');
      } finally {
        messageLoading.value = false;
      }
    }

    // 修改预览图片的方法
    const previewImage = (url) => {
      if (!url) return;

      // 直接使用原始URL，不做任何替换处理
      previewImageUrl.value = url;
      previewVisible.value = true;
    };

    // 关闭预览
    const closePreview = () => {
      previewVisible.value = false;
    };

    // 从文件URL中获取文件名
    const getFileName = (fileUrl) => {
      if (!fileUrl) return '未知文件';

      try {
        // 尝试从URL中提取文件名
        const url = new URL(fileUrl);
        const pathParts = url.pathname.split('/');
        const fileName = pathParts[pathParts.length - 1];

        // 如果文件名中有查询参数，去除它们
        return decodeURIComponent(fileName.split('?')[0]);
      } catch (e) {
        // 如果URL解析失败，直接返回最后一部分
        const parts = fileUrl.split('/');
        return parts[parts.length - 1].split('?')[0] || '文件';
      }
    }

    // 下载文件
    const downloadFile = (fileUrl) => {
      downloadFileByUrl(fileUrl);
    }

    // 打开图片选择器
    const openImageSelector = () => {
      if (!selectedFriend.value) {
        ElMessage.warning('请先选择一个聊天对象');
        return;
      }
      imageInputRef.value.click();
    }

    // 打开文件选择器
    const openFileSelector = () => {
      if (!selectedFriend.value) {
        ElMessage.warning('请先选择一个聊天对象');
        return;
      }
      fileInputRef.value.click();
    }

    // 处理选择的图片
    const handleImageSelected = async (event) => {
      const file = event.target.files[0];
      if (!file) return;

      // 验证文件类型
      if (!file.type.startsWith('image/')) {
        ElMessage.warning('请选择图片文件');
        return;
      }

      // 验证文件大小（限制为20MB）
      if (file.size > 20 * 1024 * 1024) {
        ElMessage.warning('图片大小不能超过20MB');
        return;
      }

      try {
        // 创建临时预览
        const reader = new FileReader();

        // 生成预览URL
        const previewUrl = await new Promise((resolve, reject) => {
          reader.onload = (e) => resolve(e.target.result);
          reader.onerror = reject;
          reader.readAsDataURL(file);
        });

        // 保存临时选择的图片
        tempSelectedImage.value = {
          file: file,
          previewUrl: previewUrl
        };

        // 清空文件输入，以便下次选择同一文件时能触发change事件
        event.target.value = '';

      } catch (error) {
        console.error('读取图片失败:', error);
        ElMessage.error('读取图片失败: ' + (error.message || '未知错误'));

        // 清空文件输入
        event.target.value = '';
      }
    };

    // 处理选择的文件
    const handleFileSelected = async (event) => {
      const file = event.target.files[0];
      if (!file) return;

      // 验证文件大小（限制为1GB，与后端配置一致）
      if (file.size > 1024 * 1024 * 1024) {
        ElMessage.warning('文件大小不能超过1GB');
        return;
      }

      try {
        // 检查文件类型，如果是图片或视频，自动处理为对应类型
        let fileType = MessageType.FILE; // 默认为普通文件类型

        // 通过MIME类型判断
        if (file.type.startsWith('image/')) {
          // 如果是图片，应该使用handleImageSelected处理
          ElMessage.warning('您选择的是图片文件，请使用图片上传功能');
          return;
        } else if (file.type.startsWith('video/')) {
          // 如果是视频，设置为视频类型
          fileType = MessageType.VIDEO;
        }

        // 如果MIME类型不准确，通过文件扩展名再次检查
        const fileName = file.name.toLowerCase();
        if (/\.(jpe?g|png|gif|bmp|webp)$/i.test(fileName)) {
          // 是图片文件
          ElMessage.warning('您选择的是图片文件，请使用图片上传功能');
          return;
        } else if (/\.(mp4|avi|mov|wmv|mkv|webm|m3u8|flv)$/i.test(fileName)) {
          // 是视频文件
          fileType = MessageType.VIDEO;
        }

        // 保存临时选择的文件，同时记录类型
        tempSelectedFile.value = {
          file: file,
          type: fileType
        };

        // 清空文件输入，以便下次选择同一文件时能触发change事件
        event.target.value = '';

      } catch (error) {
        console.error('读取文件失败:', error);
        ElMessage.error('读取文件失败: ' + (error.message || '未知错误'));

        // 清空文件输入
        event.target.value = '';
      }
    };

    // 修改getMessageImageUrl方法，直接使用filePath
    const getMessageImageUrl = (message) => {
      // 如果是上传中的临时消息，直接返回内容(base64数据)
      if (message.isUploading) {
        return message.content;
      }

      // 优先使用filePath字段
      if (message.filePath) {
        return message.filePath; // 直接返回原始URL，不做任何替换
      }

      // 如果content看起来是一个完整的URL，直接使用
      if (message.content && (message.content.startsWith('http://') || message.content.startsWith('https://'))) {
        return message.content;
      }

      // 最后才尝试使用fileId
      if (message.fileId) {
        return getFileUrl(message.fileId);
      }

      // 否则尝试使用content作为fileId
      return getFileUrl(message.content);
    }

    // 格式化图片URL - 这个函数不再使用，直接返回原始URL
    const formatImageUrl = (url) => {
      return url || '';
    };

    // 修改getMessageFileUrl方法，优先使用filePath
    const getMessageFileUrl = (message) => {
      // 如果消息有filePath字段，直接使用
      if (message.filePath) {
        return message.filePath;
      }

      // 如果content看起来是一个完整的URL，直接使用
      if (message.content && (message.content.startsWith('http://') || message.content.startsWith('https://'))) {
        return message.content;
      }

      // 优先使用fileId
      if (message.fileId) {
        return getFileUrl(message.fileId);
      }

      // 否则尝试使用content作为fileId
      return getFileUrl(message.content);
    }

    // 修复重复声明downloadUrl变量的问题
    const downloadFileByUrl = (fileUrl, event, expireTime) => {
      // 如果事件对象存在，阻止事件冒泡
      if (event) {
        event.preventDefault();
        event.stopPropagation();
      }

      // 检查文件是否过期
      if (isFileExpired(expireTime)) {
        ElMessage.error('文件已过期，无法下载');
        return;
      }

      if (!fileUrl) {
        ElMessage.error('文件链接无效');
        return;
      }

      try {
        // 确保fileUrl是完整的URL
        const fullUrl = fileUrl.startsWith('http') ? fileUrl : `http://${fileUrl}`;

        // 获取token
        const token = localStorage.getItem('token');

        // 构建下载URL，添加token认证
        const downloadUrl = `${process.env.VUE_APP_BASE_API || ''}/api/file/download?fileUrl=${encodeURIComponent(fullUrl)}${token ? '&token=' + token : ''}`;

        // 使用隐藏的a标签下载，避免打开新窗口
        const link = document.createElement('a');
        link.href = downloadUrl;
        link.target = '_blank';
        link.style.display = 'none';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      } catch (e) {
        console.error('下载文件失败:', e);
        ElMessage.error('下载文件失败: ' + (e.message || '未知错误'));
      }
    };

    // 添加格式化文件大小的函数
    const formatFileSize = (sizeInBytes) => {
      // 确保输入是数字
      const size = Number(sizeInBytes);
      if (isNaN(size)) return '未知大小';

      if (size < 1024) {
        return size + ' B';
      } else if (size < 1024 * 1024) {
        return (size / 1024).toFixed(2) + ' KB';
      } else if (size < 1024 * 1024 * 1024) {
        return (size / (1024 * 1024)).toFixed(2) + ' MB';
      } else {
        return (size / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
      }
    };

    // 添加检查文件是否过期的函数
    const isFileExpired = (expireTime) => {
      if (!expireTime) return false;
      try {
        const expireDate = new Date(expireTime);
        const now = new Date();
        return expireDate < now;
      } catch (e) {
        console.error('解析过期时间出错:', e);
        return false;
      }
    };

    // 处理图片加载错误
    // 修复图片错误处理 - 防止重复弹窗
    const handleImageError = (event, message) => {
      const imageUrl = message.filePath || message.content

      // 如果已经为这个URL显示过错误消息，就不再显示
      if (imageErrorMessages.value.has(imageUrl)) {
        return
      }

      // 记录已显示错误的URL
      imageErrorMessages.value.add(imageUrl)

      // 设置5秒后清除记录，允许重新显示错误（如果用户刷新等）
      setTimeout(() => {
        imageErrorMessages.value.delete(imageUrl)
      }, 5000)

      console.error('图片加载失败:', imageUrl)

      // 设置默认占位图
      event.target.src = ''

      // 只显示一次错误提示
      ElMessage.error({
        message: '图片加载失败，可能是网络问题或图片已过期',
        duration: 3000,
        showClose: true
      })
    }

    // 获取图片URL - 修复图片显示问题
    const getImageUrl = (message) => {
      // 如果是上传中的临时消息，直接返回内容(base64数据)
      if (message.isUploading) {
        return message.content
      }

      // 优先使用filePath字段
      if (message.filePath) {
        // 添加时间戳作为缓存破坏参数，确保图片能正确加载
        const url = new URL(message.filePath)
        url.searchParams.set('t', Date.now())
        return url.toString()
      }

      // 如果content看起来是一个完整的URL，直接使用并添加缓存破坏参数
      if (message.content && (message.content.startsWith('http://') || message.content.startsWith('https://'))) {
        const url = new URL(message.content)
        url.searchParams.set('t', Date.now())
        return url.toString()
      }

      // 最后才尝试使用fileId
      if (message.fileId) {
        return getFileUrl(message.fileId) + '?t=' + Date.now()
      }

      // 否则尝试使用content作为fileId
      return getFileUrl(message.content) + '?t=' + Date.now()
    }

// 获取图片的唯一key，用于强制重新渲染
    const getImageKey = (message) => {
      return `${message.id}-${message.filePath || message.content}-${Date.now()}`
    }

// 图片加载成功处理
    const handleImageLoad = (event, message) => {
      const imageUrl = message.filePath || message.content
      // 图片加载成功时，清除错误记录
      imageErrorMessages.value.delete(imageUrl)
    }

    // 检查文件是否为视频
    const isVideoFile = (fileName) => {
      if (!fileName) return false;
      const videoExtensions = ['.mp4', '.avi', '.mov', '.wmv', '.mkv', '.webm', '.m3u8'];
      return videoExtensions.some(ext => fileName.toLowerCase().endsWith(ext));
    }

    // 预览视频
    const previewVideo = (url, videoElement) => {
      if (!url) return;

      // 直接对点击的视频元素请求全屏
      if (videoElement && videoElement.requestFullscreen) {
        videoElement.requestFullscreen()
            .catch(err => {
              console.error('全屏播放失败:', err);
              ElMessage.warning('全屏播放失败，请尝试直接点击视频播放器的全屏按钮');
            });
      } else if (videoElement && videoElement.webkitRequestFullscreen) {
        videoElement.webkitRequestFullscreen();
      } else if (videoElement && videoElement.msRequestFullscreen) {
        videoElement.msRequestFullscreen();
      } else {
        ElMessage.warning('您的浏览器不支持全屏API，请尝试使用视频控制器的全屏按钮');
      }
    };

    // 关闭视频预览
    const closeVideoPreview = () => {
      videoPreviewVisible.value = false;
      videoPreviewUrl.value = '';
    };

    // 切换全屏状态
    const toggleFullScreen = () => {
      if (fullscreenVideoRef.value) {
        try {
          if (document.fullscreenElement) {
            // 如果已经是全屏状态，则退出全屏
            document.exitFullscreen();
          } else {
            // 进入全屏状态
            if (fullscreenVideoRef.value.requestFullscreen) {
              fullscreenVideoRef.value.requestFullscreen();
            } else if (fullscreenVideoRef.value.webkitRequestFullscreen) {
              fullscreenVideoRef.value.webkitRequestFullscreen();
            } else if (fullscreenVideoRef.value.msRequestFullscreen) {
              fullscreenVideoRef.value.msRequestFullscreen();
            }
          }
        } catch (err) {
          console.error('全屏切换失败:', err);
          ElMessage.error('全屏切换失败，请使用浏览器内置的全屏功能');
        }
      }
    };

    // 添加监听视频元素变化的方法
    const observeVideoElements = () => {
      setTimeout(() => {
        const videos = document.querySelectorAll('.message-video');
        videos.forEach(video => {
          registerVideo(video);
        });
      }, 200);
    };

    // 当消息列表更新后，重新注册视频元素
    watch(messageList, () => {
      observeVideoElements();
    });

    // 播放语音消息
    const playAudio = (message) => {
      // 检查是否已过期
      if (isFileExpired(message.expireTime)) {
        ElMessage.warning('语音消息已过期')
        return
      }

      // 如果当前有正在播放的，先将其标记为非播放状态
      const playingMessage = messageList.value.find(m => m.isPlaying)
      if (playingMessage) {
        playingMessage.isPlaying = false
      }

      // 标记当前消息为播放状态
      message.isPlaying = true

      // 创建音频元素播放
      const audio = new Audio(message.filePath)

      // 播放结束后，重置播放状态
      audio.onended = () => {
        message.isPlaying = false
      }

      // 错误处理
      audio.onerror = (e) => {
        console.error('播放语音消息失败:', e)
        message.isPlaying = false
        ElMessage.error('播放语音消息失败')
      }

      // 开始播放
      audio.play()
    }

    // 格式化语音时长
    const formatDuration = (seconds) => {
      const minutes = Math.floor(seconds / 60)
      const remainingSeconds = Math.floor(seconds % 60)
      return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`
    }

    // 开始录制语音
    const startVoiceRecording = async () => {
      if (!selectedFriend.value) {
        ElMessage.warning('请先选择一个聊天对象')
        return
      }

      try {
        // 开始录音
        await audioRecorder.startRecording(60000) // 最大录制60秒

        // 更新状态
        isRecording.value = true
        recordingDuration.value = 0

        // 弹出录音中提示
        ElMessage({
          type: 'info',
          message: '正在录音...',
          duration: 0,
          showClose: true
        })

        // 开始计时
        recordingTimer.value = setInterval(() => {
          recordingDuration.value += 1

          // 超过60秒自动停止
          if (recordingDuration.value >= 60) {
            stopVoiceRecording()
          }
        }, 1000)

        // 添加录音控制按钮到消息区域
        showRecordingControls()

      } catch (error) {
        console.error('开始录音失败:', error)
        ElMessage.error('开始录音失败: ' + error.message)
      }
    }

    // 停止录制语音
    const stopVoiceRecording = async () => {
      if (!isRecording.value) return

      try {
        // 停止计时
        clearInterval(recordingTimer.value)

        // 停止录音并获取录音数据
        const {blob, duration} = await audioRecorder.stopRecording()

        // 关闭录音提示
        ElMessage.closeAll()

        // 如果录音时间太短
        if (duration < 1) {
          ElMessage.warning('录音时间太短，请重新录制')
          isRecording.value = false
          recordingDuration.value = 0
          hideRecordingControls()
          return
        }

        // 创建音频文件对象
        const audioFile = audioRecorder.createAudioFile(blob, duration)

        // 保存临时音频文件
        tempAudioFile.value = {
          file: audioFile,
          duration: duration
        }

        // 更新UI状态
        isRecording.value = false
        hideRecordingControls()

        // 显示预览和确认发送对话框
        showAudioPreview(blob, duration)

      } catch (error) {
        console.error('停止录音失败:', error)
        ElMessage.error('停止录音失败: ' + error.message)

        // 重置状态
        isRecording.value = false
        recordingDuration.value = 0
        hideRecordingControls()
      }
    }

    // 取消录音
    const cancelVoiceRecording = () => {
      if (!isRecording.value) return

      // 停止计时
      clearInterval(recordingTimer.value)

      // 取消录音
      audioRecorder.cancelRecording()

      // 关闭录音提示
      ElMessage.closeAll()

      // 重置状态
      isRecording.value = false
      recordingDuration.value = 0
      hideRecordingControls()

      ElMessage.info('已取消录音')
    }

    // 显示录音控制界面
    const showRecordingControls = () => {
      // 在聊天输入区域显示录音控制按钮
      // 这里可以通过DOM操作或状态管理添加录音控制界面
      // 简化实现，直接使用消息提示
    }

    // 隐藏录音控制界面
    const hideRecordingControls = () => {
      // 隐藏录音控制界面
    }

    // 显示音频预览
    const showAudioPreview = (blob, duration) => {
      // 创建预览URL
      const previewUrl = audioRecorder.createAudioPreviewUrl(blob)

      // 弹出确认对话框
      ElMessageBox.confirm(
          `<div class="audio-preview">
          <div>录音时长: ${formatDuration(duration)}</div>
          <audio controls src="${previewUrl}"></audio>
        </div>`,
          '发送语音消息',
          {
            confirmButtonText: '发送',
            cancelButtonText: '取消',
            dangerouslyUseHTMLString: true,
            type: 'info',
          }
      ).then(() => {
        // 用户确认发送
        sendAudioMessage()
      }).catch(() => {
        // 用户取消
        tempAudioFile.value = null
        URL.revokeObjectURL(previewUrl) // 释放URL
      })
    }

    // 发送语音消息
    const sendAudioMessage = async () => {
      if (!tempAudioFile.value) return

      try {
        const tempId = 'temp-' + Date.now()
        isUploading.value = true
        uploadProgress.value = 0

        // 创建临时消息对象显示在UI上
        const tempMessage = {
          id: tempId,
          senderUserId: userInfo.id,
          receiverUserId: selectedFriend.value.userId,
          content: '',
          fileName: tempAudioFile.value.file.name,
          fileSize: tempAudioFile.value.file.size,
          duration: tempAudioFile.value.duration,
          msgType: MessageType.AUDIO,
          createTime: new Date().toISOString(),
          status: 0, // 未读状态
          isUploading: true, // 标记为正在上传
          uploadProgress: 0 // 上传进度
        }

        // 添加到消息列表，立即显示
        messageList.value.push(tempMessage)

        // 滚动到底部
        scrollToBottom()

        // 显示上传进度提示
        const loadingInstance = ElMessage({
          type: 'info',
          message: `正在上传语音消息...`,
          duration: 0,
          showClose: true
        })

        // 上传文件到服务器
        const response = await uploadFile(tempAudioFile.value.file, {
          onProgress: (progress) => {
            // 更新上传进度
            uploadProgress.value = progress
            // 更新消息列表中的进度显示
            const index = messageList.value.findIndex(m => m.id === tempId)
            if (index !== -1) {
              messageList.value[index].uploadProgress = progress
            }
          },
          extraData: {
            msgType: MessageType.AUDIO, // 将消息类型传递给上传函数
            duration: tempAudioFile.value.duration // 传递语音时长
          }
        })

        // 关闭上传提示
        loadingInstance.close()
        ElMessage.closeAll()

        if (!response) {
          throw new Error('上传响应格式不正确')
        }

        // 获取文件ID和路径
        const {id: fileId, path: filePath} = response

        // 更新临时消息
        const messageIndex = messageList.value.findIndex(m => m.id === tempId)
        if (messageIndex !== -1) {
          messageList.value[messageIndex] = {
            ...messageList.value[messageIndex],
            id: response.messageId || 'msg-' + Date.now(),
            fileId: fileId,
            filePath: filePath,
            content: filePath,
            isUploading: false,
            uploadProgress: 100
          }
        }

        // 保存必要信息
        const fileName = tempAudioFile.value.file.name
        const fileSize = tempAudioFile.value.file.size
        const duration = tempAudioFile.value.duration

        // 清空临时文件
        tempAudioFile.value = null
        isUploading.value = false

        // 发送消息到服务器
        const msgResult = await sendMessageRequest({
          receiverUserId: selectedFriend.value.userId,
          content: filePath,
          msgType: MessageType.AUDIO,
          fileId: fileId,
          filePath: filePath,
          fileName: fileName,
          fileSize: fileSize,
          duration: duration
        })

        // 如果需要，根据返回结果更新临时消息
        if (msgResult && msgResult.id) {
          const index = messageList.value.findIndex(m => m.id === tempId)
          if (index !== -1) {
            messageList.value[index] = {
              ...msgResult,
              status: msgResult.status !== undefined ? msgResult.status : 0
            }
          }
        }

        ElMessage.success('语音消息发送成功')

      } catch (error) {
        console.error('发送语音消息失败:', error)
        ElMessage.error('语音消息发送失败: ' + (error.message || '未知错误'))

        // 重置上传状态
        isUploading.value = false
        uploadProgress.value = 0
        tempAudioFile.value = null
      }
    }

    return {
      activeMenu,
      showProfile,
      userInfo,
      userProfile,
      loading,
      friendList,
      selectedFriend,
      searchQuery,
      messageText,
      menuLabels,
      filteredFriends,
      filteredContacts,
      profileFormRef,
      profileRules,
      showAddFriendDialog,
      handleMenuSelect,
      selectFriend,
      sendMessage,
      uploadAvatar,
      saveProfile,
      handleLogout,
      fetchUserInfo,
      fetchFriends,
      pagination,
      loadMoreFriends,
      searchResult,
      searchLoading,
      handleSearch,
      handleAddFriend,
      friendSearchQuery,
      friendRequests,
      requestLoading,
      fetchFriendRequests,
      handleFriendRequestAction,
      viewFriendRequests,
      friendRequestCount,
      fetchFriendRequestCount,
      handleFriendCommand,
      confirmDeleteFriend,
      handleUpdateRemark,
      viewFriendDetail,
      friendDetail,
      showFriendDetail,
      loadingFriendDetail,
      messageList,
      messageLoading,
      formatDate,
      scrollToBottom,
      unreadMessages,
      handleChatScroll,
      messagePagination,
      loadMoreMessages,
      showSearchUserDialog,
      closeSearchDialog,
      contactsSearchQuery,
      handleChatCommand,
      previewImage,
      getFileName,
      downloadFile,
      MessageType,
      imageInputRef,
      fileInputRef,
      openImageSelector,
      openFileSelector,
      handleImageSelected,
      handleImageLoad,
      getImageUrl,
      getImageKey,
      handleFileSelected,
      getMessageImageUrl,
      getMessageFileUrl,
      tempSelectedImage,
      downloadFileByUrl,
      previewVisible,
      previewImageUrl,
      closePreview,
      formatFileSize,
      isFileExpired,
      tempSelectedFile,
      uploadProgress,
      isUploading,
      handleImageError,
      formatImageUrl,
      isVideoFile,
      previewVideo,
      videoPreviewVisible,
      videoPreviewUrl,
      videoPreviewTitle,
      closeVideoPreview,
      fullscreenVideoRef,
      toggleFullScreen,
      // 语音消息相关
      isRecording,
      recordingDuration,
      startVoiceRecording,
      stopVoiceRecording,
      cancelVoiceRecording,
      formatDuration,
      playAudio
    }
  }
}
</script>

<style scoped>
.home-container {
  display: flex;
  height: 100vh;
  width: 100%;
  overflow: hidden;
}

/* 侧边栏样式 */
.sidebar {
  width: 280px;
  height: 100%;
  background: #fff;
  border-right: 1px solid #eee;
  display: flex;
  flex-direction: column;
}

.user-profile {
  display: flex;
  align-items: center;
  padding: 20px;
  border-bottom: 1px solid #eee;
  cursor: pointer;
}

.avatar {
  margin-right: 15px;
}

.user-info {
  flex: 1;
}

.nickname {
  font-size: 16px;
  font-weight: 500;
  color: #333;
  margin-bottom: 5px;
}

.tf-id {
  font-size: 12px;
  color: #999;
}

.menu {
  flex: 1;
  border-right: none;
}

.menu :deep(.el-menu-item) {
  height: 50px;
  line-height: 50px;
}

.menu :deep(.el-menu-item.is-active) {
  background-color: rgba(118, 75, 162, 0.1);
  color: #764ba2;
}

.menu :deep(.el-menu-item:hover) {
  background-color: rgba(118, 75, 162, 0.05);
}

.logout {
  display: flex;
  align-items: center;
  padding: 15px 20px;
  border-top: 1px solid #eee;
  color: #f56c6c;
  cursor: pointer;
}

.logout .el-icon {
  margin-right: 10px;
}

/* 主内容区样式 */
.main-content {
  flex: 1;
  display: flex;
  height: 100%;
  background-color: #f9f9f9;
  position: relative;
}

/* 好友列表 */
.friend-list {
  width: 300px;
  height: 100%;
  background: #fff;
  border-right: 1px solid #eee;
  display: flex;
  flex-direction: column;
}

.friend-list-header {
  padding: 15px 20px;
  border-bottom: 1px solid #eee;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.friend-actions-header {
  display: flex;
  justify-content: flex-start;
  gap: 10px;
}

.friend-search {
  width: 100%;
}

.friend-list-content {
  flex: 1;
  overflow-y: auto;
}

.friend-item {
  display: flex;
  align-items: center;
  padding: 15px;
  border-bottom: 1px solid #f5f5f5;
  cursor: pointer;
}

.friend-item:hover {
  background-color: #f9f9f9;
}

.friend-item.active {
  background-color: rgba(118, 75, 162, 0.1);
}

.avatar-container {
  position: relative;
}

.unread-badge {
  position: absolute;
  top: -5px;
  right: -5px;
  z-index: 1;
}

.friend-info {
  flex: 1;
  margin-left: 10px;
}

.friend-name {
  font-size: 15px;
  color: #333;
  margin-bottom: 3px;
  display: flex;
  align-items: center;
}

.friend-status {
  font-size: 12px;
  color: #999;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.friend-actions {
  display: flex;
  align-items: center;
}

.friend-more {
  color: #999;
  cursor: pointer;
}

/* 聊天区域 */
.chat-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  height: 100%;
}

.chat-header {
  height: 65px;
  background: #fff;
  border-bottom: 1px solid #eee;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
}

.chat-title {
  font-size: 16px;
  font-weight: 500;
}

.chat-toolbar {
  display: flex;
  gap: 20px;
}

.toolbar-icon {
  font-size: 20px;
  color: #666;
  cursor: pointer;
}

.toolbar-icon:hover {
  color: #764ba2;
}

.chat-messages {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
  background-color: #f9f9f9;
  display: flex;
  flex-direction: column;
}

.chat-input-area {
  background: #fff;
  padding: 15px;
  border-top: 1px solid #eee;
  display: flex;
  flex-direction: column;
}

.toolbar {
  display: flex;
  gap: 15px;
  margin-bottom: 10px;
}

.toolbar .el-icon {
  font-size: 20px;
  color: #666;
  cursor: pointer;
}

.chat-input-area .el-button {
  align-self: flex-end;
  margin-top: 10px;
  background: linear-gradient(to right, #667eea, #764ba2);
  border: none;
}

/* 空聊天区域 */
.empty-chat {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #f9f9f9;
}

.empty-chat-content {
  text-align: center;
  color: #999;
}

.empty-icon {
  margin-bottom: 15px;
  color: #ddd;
}

/* 其他功能占位内容 */
.placeholder-content {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 个人资料弹窗 */
.profile-content {
  padding: 20px 0;
}

.profile-avatar {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 30px;
}

.avatar-upload {
  margin-top: 15px;
}

:deep(.el-button--primary) {
  --el-button-bg-color: #764ba2;
  --el-button-border-color: #764ba2;
  --el-button-hover-bg-color: #8a63b1;
  --el-button-hover-border-color: #8a63b1;
  --el-button-active-bg-color: #673d91;
}

:deep(.el-radio__input.is-checked .el-radio__inner) {
  background-color: #764ba2;
  border-color: #764ba2;
}

:deep(.el-radio__input.is-checked + .el-radio__label) {
  color: #764ba2;
}

.load-more {
  text-align: center;
  padding: 10px 0;
  border-bottom: 1px solid #f5f5f5;
}

.load-more .el-button {
  width: 100%;
  display: block;
  color: #764ba2;
}

.el-empty {
  padding: 40px 0;
}

/* 搜索结果区域样式 */
.search-result-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  height: 100%;
  background-color: #f9f9f9;
}

.search-result-content {
  flex: 1;
  padding: 20px;
  display: flex;
  flex-direction: column;
}

.search-result-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.search-result-header h3 {
  margin: 0;
  font-size: 18px;
  color: #333;
}

.search-result-item {
  display: flex;
  align-items: flex-start;
  padding: 20px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.search-result-info {
  flex: 1;
  margin-left: 20px;
}

.search-result-name {
  font-size: 18px;
  font-weight: 500;
  color: #333;
  margin-bottom: 10px;
}

.search-result-tfid {
  font-size: 14px;
  color: #999;
  margin-left: 10px;
}

.search-result-email {
  font-size: 14px;
  color: #666;
  margin-bottom: 10px;
}

.search-result-signature {
  font-size: 14px;
  color: #999;
  margin-bottom: 15px;
  font-style: italic;
}

.search-result-actions {
  margin-top: 15px;
}

/* 好友请求列表样式 */
.friend-requests {
  margin-bottom: 20px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.friend-requests-header {
  padding: 15px;
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.friend-requests-header h3 {
  margin: 0;
  font-size: 16px;
  color: #333;
}

.empty-requests {
  padding: 30px;
}

.request-items {
  padding: 10px;
}

.request-item {
  display: flex;
  align-items: center;
  padding: 10px;
  border-bottom: 1px solid #eee;
}

.request-item:last-child {
  border-bottom: none;
}

.request-info {
  flex: 1;
  margin-left: 15px;
}

.request-name {
  font-size: 14px;
  color: #333;
  margin-bottom: 5px;
}

.request-tfid {
  font-size: 12px;
  color: #999;
  margin-left: 10px;
}

.request-email {
  font-size: 12px;
  color: #666;
  margin-bottom: 5px;
}

.request-time {
  font-size: 12px;
  color: #999;
}

.request-actions {
  margin-left: 15px;
}

.request-actions .el-button {
  margin-left: 10px;
}

/* 好友请求徽章样式 */
.friend-request-btn {
  position: relative;
}

.request-badge {
  position: absolute !important;
  top: -8px !important;
  right: -8px !important;
  transform: translate(0, 0) !important;
}

/* 好友详情弹窗样式 */
.friend-detail-content {
  padding: 20px 0;
}

.friend-detail-avatar {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 30px;
}

.friend-detail-info {
  padding: 0 20px;
}

.detail-item {
  margin-bottom: 10px;
}

.label {
  font-weight: 500;
}

.value {
  margin-left: 10px;
}

.friend-detail-form {
  padding: 0 20px;
}

.friend-detail-form .el-form-item {
  margin-bottom: 20px;
}

.friend-detail-form .el-form-item label {
  font-weight: 500;
}

.friend-detail-form .el-form-item .el-input {
  width: 100%;
}

.friend-detail-form .el-form-item .el-button {
  width: 100%;
  background: linear-gradient(to right, #667eea, #764ba2);
  border: none;
  color: #fff;
}

.friend-detail-form .el-form-item .el-button:hover {
  background: linear-gradient(to right, #8a63b1, #a270b8);
}

/* 消息列表样式 */
.message-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 15px;
  padding: 10px;
}

.message-item {
  display: flex;
  align-items: flex-start;
  max-width: 70%;
}

.message-item.self {
  align-self: flex-end;
  flex-direction: row-reverse;
}

.message-item.other {
  align-self: flex-start;
}

.message-content {
  margin: 0 10px;
  background: #fff;
  padding: 10px 15px;
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  position: relative;
}

.message-item.self .message-content {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}

.message-text {
  font-size: 14px;
  line-height: 1.5;
  word-break: break-word;
}

.message-image {
  max-width: 200px;
  max-height: 200px;
  overflow: hidden;
  border-radius: 4px;
}

.message-time {
  font-size: 12px;
  color: #909399;
  margin-top: 5px;
  display: flex;
  align-items: center;
}

.self .read-status {
  color: #95a5a6;
}

.self .message-time {
  justify-content: flex-end;
}

.chat-placeholder {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

/* 加载更多消息样式 */
.load-more-messages {
  width: 100%;
  text-align: center;
  padding: 10px 0;
  margin-bottom: 10px;
}

.loading-indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #999;
  font-size: 12px;
}

.loading-icon {
  animation: rotate 1s linear infinite;
  margin-right: 5px;
}

.load-more-btn {
  color: #764ba2;
  font-size: 13px;
  cursor: pointer;
  padding: 5px 0;
}

.load-more-btn:hover {
  color: #667eea;
}

.search-user-form {
  padding: 20px 0;
}

.search-tip {
  margin-bottom: 15px;
  color: #666;
}

.search-input {
  width: 100%;
}

.search-result-container {
  padding: 20px 0;
}

.search-result-user {
  display: flex;
  margin-bottom: 20px;
}

.search-result-info {
  margin-left: 20px;
  flex: 1;
}

.search-result-name {
  font-size: 18px;
  font-weight: 500;
  margin-bottom: 10px;
}

.search-result-tfid {
  font-size: 14px;
  color: #999;
  margin-left: 10px;
}

.search-result-email {
  font-size: 14px;
  color: #666;
  margin-bottom: 10px;
}

.search-result-signature {
  font-size: 14px;
  color: #999;
  margin-bottom: 15px;
  font-style: italic;
}

.search-result-actions {
  display: flex;
  justify-content: center;
  margin-top: 20px;
}

/* 已读状态样式 */
.read-status {
  font-size: 12px;
  margin-left: 8px;
  color: #909399;
  display: inline-block;
  transition: all 0.3s;
}

/* 消息已读和未读状态样式差异 */
.read-status:not([data-read="true"]) {
  color: #95a5a6;
}

/* 调整时间和已读状态的显示 */
.message-time {
  font-size: 12px;
  color: #909399;
  margin-top: 5px;
  display: flex;
  align-items: center;
}

/* 控制已读状态位置 */
.self .message-time {
  justify-content: flex-end;
}

/* 其他人发送的消息样式调整 */
.other .message-time {
  color: #909399;
}

/* 自己发送的消息样式调整 */
.self .message-time {
  color: rgba(255, 255, 255, 0.8);
}

.self .read-status {
  color: rgba(255, 255, 255, 0.7);
}

/* 通讯录样式 */
.contacts-container {
  flex: 1;
  display: flex;
  height: 100%;
  overflow: hidden;
}

.contacts-list {
  flex: 1;
  display: flex;
  flex-direction: column;
  background-color: #fff;
}

.contacts-header {
  padding: 15px 20px;
  border-bottom: 1px solid #eee;
}

.contacts-header h3 {
  margin: 0 0 15px 0;
  font-size: 18px;
  color: #333;
}

.contacts-search {
  margin-bottom: 10px;
}

.contacts-content {
  flex: 1;
  overflow-y: auto;
  padding: 0 10px;
}

.contact-item {
  display: flex;
  align-items: center;
  padding: 15px;
  border-bottom: 1px solid #f5f5f5;
  transition: background-color 0.2s;
}

.contact-item:hover {
  background-color: #f9f9f9;
}

.contact-avatar {
  margin-right: 15px;
}

.contact-info {
  flex: 1;
}

.contact-name {
  font-size: 16px;
  font-weight: 500;
  color: #333;
  margin-bottom: 5px;
}

.contact-tfid {
  font-size: 13px;
  color: #999;
  margin-left: 10px;
  font-weight: normal;
}

.contact-email {
  font-size: 13px;
  color: #666;
  margin-bottom: 5px;
}

.contact-signature {
  font-size: 13px;
  color: #999;
  font-style: italic;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 400px;
}

.contact-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* 图片消息样式 */
.message-image {
  max-width: 200px;
  max-height: 200px;
  border-radius: 8px;
  cursor: pointer;
  transition: transform 0.2s;
}

.message-image:hover {
  transform: scale(1.03);
}

/* 图片预览对话框样式 */
.image-preview-dialog .el-message-box__content {
  padding: 10px;
  text-align: center;
}

.image-preview {
  max-height: 80vh;
  overflow: auto;
}

/* 文件消息样式 */
.file-message {
  display: flex;
  align-items: center;
  background: rgba(0, 0, 0, 0.05);
  border-radius: 8px;
  padding: 10px;
  margin: 5px 0;
}

.file-icon {
  font-size: 24px;
  margin-right: 10px;
  color: #5c6bc0;
}

.file-info {
  flex: 1;
}

.file-name {
  font-size: 14px;
  margin-bottom: 5px;
  word-break: break-all;
}

/* 调整其他消息元素的样式 */
.self .file-message {
  background: rgba(255, 255, 255, 0.2);
}

.self .file-icon {
  color: rgba(255, 255, 255, 0.9);
}

.toolbar-action {
  font-size: 22px;
  color: #666;
  cursor: pointer;
  transition: all 0.3s;
}

.toolbar-action:hover {
  color: #764ba2;
  transform: scale(1.1);
}

/* 临时图片预览样式 */
.temp-image-preview {
  position: relative;
  margin-bottom: 10px;
  display: inline-block;
  max-width: 120px;
  max-height: 120px;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid #dcdfe6;
}

.temp-preview-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.temp-image-actions {
  position: absolute;
  top: 5px;
  right: 5px;
  background-color: rgba(0, 0, 0, 0.5);
  border-radius: 50%;
  padding: 3px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.temp-image-delete {
  color: #fff;
  font-size: 16px;
  cursor: pointer;
}

/* 图片容器样式 */
.image-container {
  position: relative;
  display: inline-block;
}

.download-button {
  position: absolute;
  right: 8px;
  bottom: 8px;
  opacity: 0;
  transition: opacity 0.3s;
  display: flex;
  align-items: center;
}

.download-button .el-icon {
  margin-right: 4px;
}

/* 文件下载按钮样式 */
.file-download-button {
  margin-left: 10px;
  display: flex;
  align-items: center;
}

.file-download-button .el-icon {
  margin-right: 4px;
}

/* 添加回图片悬停显示下载按钮的样式 */
.image-container:hover .download-button {
  opacity: 1;
}

/* 添加回文件消息的基本样式 */
.file-message {
  display: flex;
  align-items: center;
  background: rgba(0, 0, 0, 0.05);
  border-radius: 8px;
  padding: 10px;
  margin: 5px 0;
}

/* 文件信息覆盖层样式 */
.file-info-overlay {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  padding: 4px 8px;
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
  font-size: 12px;
  opacity: 0;
  transition: opacity 0.3s;
}

.image-container:hover .file-info-overlay {
  opacity: 1;
}

.file-name-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
}

.file-size-text {
  font-size: 11px;
  opacity: 0.8;
}

/* 文件消息元数据样式 */
.file-meta {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #666;
  margin-top: 3px;
}

.file-size {
  font-weight: 500;
}

.file-expire {
  font-style: italic;
  margin-left: 10px;
}

/* 文件过期相关样式 */
.expired {
  opacity: 0.7;
  filter: grayscale(0.8);
}

.file-expired-text {
  color: #ff4d4f;
  font-weight: bold;
  margin-top: 3px;
}

.file-expire.expired {
  color: #ff4d4f;
  font-weight: bold;
}

/* 过期文件和图片的样式 */
.expired-image-container, .expired-file-message {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: #f5f5f5;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  padding: 20px;
  width: 200px;
  max-width: 100%;
  margin: 5px 0;
}

.expired-icon {
  font-size: 32px;
  color: #bfbfbf;
  margin-bottom: 10px;
}

.expired-text {
  color: #ff4d4f;
  font-size: 14px;
  margin: 5px 0 0 0;
  font-weight: bold;
}

.expired-file-message {
  flex-direction: row;
  width: auto;
  min-width: 200px;
}

.expired-file-message .expired-icon {
  margin-right: 15px;
  margin-bottom: 0;
}

.file-expire.expired {
  color: #ff4d4f;
  font-weight: bold;
}

/* 临时文件预览样式 */
.temp-file-preview {
  position: relative;
  margin-bottom: 10px;
  display: flex;
  width: 100%;
  min-height: 60px;
  border-radius: 8px;
  border: 1px solid #dcdfe6;
  background-color: #f5f7fa;
  padding: 10px;
}

.temp-file-info {
  display: flex;
  align-items: center;
  flex: 1;
}

.file-icon {
  font-size: 24px;
  margin-right: 10px;
  color: #606266;
}

.temp-file-name {
  font-size: 14px;
  color: #303133;
  word-break: break-all;
  margin-right: 10px;
}

.temp-file-size {
  font-size: 12px;
  color: #909399;
}

.temp-file-actions {
  display: flex;
  align-items: center;
}

.temp-file-delete {
  color: #f56c6c;
  font-size: 18px;
  cursor: pointer;
  transition: all 0.3s;
}

.temp-file-delete:hover {
  transform: scale(1.1);
}

/* 添加上传进度样式 */
.upload-progress-container {
  display: flex;
  align-items: center;
  margin-top: 5px;
  width: 100%;
}

.progress-text {
  margin-left: 5px;
  font-size: 12px;
  color: #909399;
  width: 35px;
  text-align: right;
}

.file-message.uploading {
  opacity: 0.8;
  background-color: rgba(0, 0, 0, 0.02);
}

.file-message.uploading .file-name {
  color: #909399;
}

.message-video {
  max-width: 280px;
  max-height: 200px;
  border-radius: 8px;
  object-fit: contain;
  background-color: #000;
  transform: none !important; /* 防止自动旋转 */
}

/* 全屏视频样式 */
.message-video:fullscreen {
  max-width: 100%;
  max-height: 100vh;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background-color: #000;
  transform: none !important;
}

/* 适配不同浏览器的全屏前缀 */
.message-video:-webkit-full-screen {
  max-width: 100%;
  max-height: 100vh;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background-color: #000;
  transform: none !important;
}

.message-video:-moz-full-screen {
  max-width: 100%;
  max-height: 100vh;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background-color: #000;
  transform: none !important;
}

.message-video:-ms-fullscreen {
  max-width: 100%;
  max-height: 100vh;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background-color: #000;
  transform: none !important;
}

.video-container {
  position: relative;
  display: inline-block;
  border-radius: 8px;
  overflow: hidden;
  margin: 4px 0;
}

.video-container:hover .download-button {
  opacity: 1;
}

.video-preview-container {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #000;
  padding: 20px;
  box-sizing: border-box;
}

.fullscreen-video {
  max-width: 90%;
  max-height: 80vh;
  width: auto;
  height: auto;
  object-fit: contain;
}

.video-container .message-video {
  cursor: pointer;
}

/* 覆盖el-dialog的样式 */
:deep(.video-preview-dialog) {
  display: flex;
  flex-direction: column;
  background-color: rgba(0, 0, 0, 0.9);
}

:deep(.video-preview-dialog .el-dialog__header) {
  padding: 15px 20px;
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  margin: 0;
}

:deep(.video-preview-dialog .el-dialog__body) {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0;
  overflow: hidden;
}

:deep(.video-preview-dialog .el-dialog__close) {
  color: white;
  font-size: 24px;
}

:deep(.video-preview-dialog .el-dialog__close:hover) {
  color: #409EFF;
}

/* 移除之前的视频预览样式，添加新的覆盖层样式 */
.video-player-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.9);
  z-index: 9999;
  display: flex;
  flex-direction: column;
}

.video-player-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 20px;
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
}

.video-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.video-title {
  font-size: 16px;
  max-width: 70%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.close-icon {
  font-size: 24px;
  cursor: pointer;
  color: white;
}

.close-icon:hover {
  color: #409EFF;
}

.video-player-container {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 20px;
  background-color: #000000;
}

.player-video {
  max-width: 90%;
  max-height: 90vh;
  width: auto;
  height: auto;
  object-fit: contain;
  transform: none !important; /* 防止自动旋转 */
  background-color: #000000;
  border-radius: 4px;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}

.fullscreen-btn {
  margin-left: 10px;
}

/* 添加语音消息相关样式 */
.audio-message {
  display: flex;
  align-items: center;
  background: rgba(0, 0, 0, 0.05);
  border-radius: 12px;
  padding: 6px 10px;
  min-width: 120px;
  position: relative;
}

.audio-player {
  display: flex;
  align-items: center;
  width: 100%;
}

.play-icon {
  font-size: 20px;
  background: #1890ff;
  color: white;
  border-radius: 50%;
  width: 32px;
  height: 32px;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
  margin-right: 10px;
}

.audio-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  margin-right: 10px;
}

.audio-duration {
  font-size: 12px;
  color: #666;
  margin-top: 4px;
}

.audio-wave {
  display: flex;
  align-items: center;
  height: 20px;
}

.wave-bar {
  display: inline-block;
  width: 3px;
  height: 100%;
  background-color: #999;
  margin-right: 3px;
  border-radius: 1px;
}

.wave-bar:nth-child(1) {
  height: 40%;
}

.wave-bar:nth-child(2) {
  height: 60%;
}

.wave-bar:nth-child(3) {
  height: 80%;
}

.wave-bar:nth-child(4) {
  height: 60%;
}

.wave-bar:nth-child(5) {
  height: 40%;
}

.audio-wave.active .wave-bar {
  background-color: #1890ff;
  animation: sound 1.2s linear infinite alternate;
}

.audio-wave.active .wave-bar:nth-child(1) {
  animation-delay: 0.0s;
}

.audio-wave.active .wave-bar:nth-child(2) {
  animation-delay: 0.2s;
}

.audio-wave.active .wave-bar:nth-child(3) {
  animation-delay: 0.4s;
}

.audio-wave.active .wave-bar:nth-child(4) {
  animation-delay: 0.6s;
}

.audio-wave.active .wave-bar:nth-child(5) {
  animation-delay: 0.8s;
}

@keyframes sound {
  0% {
    height: 20%;
  }
  100% {
    height: 80%;
  }
}

.audio-preview {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 10px;
}

/* 录音控制界面样式 */
.recording-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: rgba(255, 255, 255, 0.9);
  border-radius: 8px;
  padding: 10px;
  margin-bottom: 10px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.recording-time {
  color: #f56c6c;
  font-weight: bold;
}

.recording-controls .el-button {
  margin-left: 10px;
}

/* 录音波形动画 */
.recording-wave {
  display: flex;
  align-items: center;
  margin: 0 10px;
  flex: 1;
}

.recording-wave-bar {
  flex: 1;
  height: 20px;
  background: linear-gradient(to bottom, #f56c6c 0%, #f56c6c 50%, transparent 100%);
  margin: 0 2px;
  border-radius: 2px 2px 0 0;
  animation: recording-wave 0.5s ease infinite alternate;
}

.recording-wave-bar:nth-child(1) {
  animation-delay: 0.0s;
}

.recording-wave-bar:nth-child(2) {
  animation-delay: 0.1s;
}

.recording-wave-bar:nth-child(3) {
  animation-delay: 0.2s;
}

.recording-wave-bar:nth-child(4) {
  animation-delay: 0.3s;
}

.recording-wave-bar:nth-child(5) {
  animation-delay: 0.4s;
}

@keyframes recording-wave {
  0% {
    height: 5px;
  }
  100% {
    height: 20px;
  }
}

/* 语音消息的下载按钮 */
.audio-message .download-button {
  position: absolute;
  right: 8px;
  opacity: 0;
  transition: opacity 0.3s;
}

.audio-message:hover .download-button {
  opacity: 1;
}
</style>